设为首页 加入收藏

TOP

Java 虚拟机16:Metaspace(一)
2018-04-14 06:06:20 】 浏览:356
Tags:Java 虚拟 Metaspace

被废弃的持久代

想起之前面试的时候有面试官问起过我一个问题:Java 8为什么要废弃持久代即Metaspace的作用。由于当时使用的Java 7且研究重心不在JVM上,一下没有回答上来,今天突然想起这个问题,就详细总结一下这个问题。

首先我们看一张JVM内存布局的图:

注意到里面有一块METHOD AREA,它是一块线程共享的对象,名为方法区,在HotSpot虚拟机中,这块METHOD AREA我们可以认为等同于持久代(PermGen),在Java 6及之前的版本,持久代存放了以下一些内容:

  • 虚拟机加载的类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码

到了Java 7之后,常量池已经不在持久代之中进行分配了,而是移到了堆中,即常量池和对象共享堆内存。

接着到了Java 8之后的版本(至此篇文章,Java 10刚发布),持久代已经被永久移除,取而代之的是Metaspace。

为什么要移除持久代

HotSpot团队选择移除持久代,有内因和外因两部分,从外因来说,我们看一下JEP 122的Motivation(动机)部分:

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

大致就是说移除持久代也是为了和JRockit进行融合而做的努力,JRockit用户并不需要配置持久代(因为JRockit就没有持久代)。

从内因来说,持久代大小受到-XX:PermSize和-XX:MaxPermSize两个参数的限制,而这两个参数又受到JVM设定的内存大小限制,这就导致在使用中可能会出现持久代内存溢出的问题,因此在Java 8及之后的版本中彻底移除了持久代而使用Metaspace来进行替代。

Metaspace

上面说了,为了避免出现持久代内存溢出的问题,Java 8及之后的版本彻底移除了持久代而使用Metaspace来进行替代。

Metaspace是方法区在HotSpot中的实现,它与持久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存。因此Metaspace具体大小理论上取决于32位/64位系统可用内存的大小,可见也不是无限制的,需要配置参数。

接着我们模拟一下Metaspace内存溢出的情况,前面说了持久代存放了以下信息:

  • 虚拟机加载的类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码

所以最简单的模拟Metaspace内存溢出,我们只需要无限生成类信息即可,类占据的空间总是会超过Metaspace指定的空间大小的,下面用Cglib来模拟:

public class MetaspaceOOMTest {

    /**
     * JVM参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=128m -XX:+PrintFlagsInitial
     */
    public static void main(String[] args) {
        int i = 0;
        
        try {
            for (;;) {
                i++;
                
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMObject.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                        return proxy.invokeSuper(obj, args);
                    }
                });
                enhancer.create();
            }
        } catch (Exception e) {
            System.out.println("第" + i + "次时发生异常");
            e.printStackTrace();
        }
    }
    
    static class OOMObject {
        
    }
    
}

虚拟机参数设置为”-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=128m“,运行代码,结果为:

第15562次时发生异常
net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
    at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
    at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
    at org.xrq.commom.test.jvm.MetaspaceOOMTest.main(MetaspaceOOMTest.java:34)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:413)
    at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336)
    ... 6 more
Caused by: java.lang.OutOfMemoryErr
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇使用 losetup 帮你创建虚拟磁盘 下一篇使用 Java 注解自动化处理对应关..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目