如果在网上搜索JVM方法区,阅读过几篇之后,你可能会发现,这些文章中的说法并不一样,有的甚至是矛盾的。原因主要是在不同的jdk版本中,方法区的实现发生了比较大的变化。

方法区

方法区(Method Area)是 Java虚拟机规范 中定义的运行时数据区域之一,它与堆(heap)一样在线程之间共享。

方法区主要用来存储运行时常量池、静态变量、类信息、JIT编译后的代码等数据。方法区是 JVM 规范所描述的抽象概念,在实际的 JVM 实现中,它不一定是由单一的特殊区域所实现,不同的实现可以放在不同的地方(参考R大的回答)。

JVM 规范摘录如下:

2.5.4. Method Area
The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface initialization. The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.

永久代(Permanent Generation)

永久代是 HotSpot 虚拟机在 JDK7 及以前对方法区的具体实现,而永久代也在 heap 中(但注意,其它虚拟机并没有永久代,这是 HotSpot 虚拟机特有的)。

在 JDK1.7 中,移除永久代的工作就已经开始了,原先存储在永久代的部分数据转移到了Java heap 或者 native memory,但永久代仍存在于 JDK1.7 中,并没完全移除。移除工作主要包括下面三点:

  • 符号引用(Symbolic Reference)转移到了native memory
  • Interned Strings转移到了heap
  • 静态变量从instanceKlass对象(PermGen内)末尾转移到了java.lang.Class对象(heap内)的末尾

元空间(metaspace)

从 JDK 8 开始的 HotSpot 虚拟机完全移除了 PermGen,改为在 native memory 里存放这些元数据。新的用于存放元数据的内存空间叫做 Metaspace。

取消永久代主要有以下几点原因:

  • 字符串存在永久代中,容易出现性能问题和内存溢出
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
  • 便于将 HotSpot 与 JRockit 合二为一(JRockit 中并没有永久代)

在 Java 8 中抛出堆溢出的示例:

public class StringHeapError {
    public static void main(String[] args) {
        String temp = "world";
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String str = temp + temp;
            temp = str;
            str.intern();
        }
    }
}

输出结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

如果是在 JDK 6 中,这会提示 PermGen Space 溢出

下面是抛出 Metaspace 溢出的例子,用了 Cglib 生成大量的代理类。不过在这之前需要修改JVM启动参数,设置为:

-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M

代码如下:

public class ClassInfo {
    public static void main(String[] args) {
        while (true) {
            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();
        }
    }

    static class OOMObject { }
}

输出结果:

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

可以看到,类信息(classes metadata)放到了 Metaspace 中,而 Interned Strings、静态变量放到了 heap 上。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐