Java8的运行时常量池与MetaSpace

Posted by on August 03, 2018

起因

近期在看《深入理解Java虚拟机:JVM高级特性与最佳实践》,在看到运行时常量池这一章,有一个例子演示运行时常量池溢出,代码如下:

/**
 * 原文Java7 VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 * Java8 VM Args: -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
 */
public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        long i = 0L;
        while(true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}

我使用Java8来运行这段程序,自然Java8把PermGen移除,取而代之是Metaspace空间,所以使用下面这个JVM参数来跑。

问题来了,程序跑的结果是过了很久才停止运行,感觉不太对。

原因

在改JVM参数的时候,考虑的是PermGen迁移到了MetaSpace,那么只需要把参数改成MetaSpace就ok了,然而事实确不是这样。

那么马上我就想到是不是运行时常量池并非从PermGen移到MetaSpace了?既然没有移到MetaSpace,那么很有可能移到Heap区了。

于是经过几次尝试,最后使用-Xms10m -Xmx10m -XX:-UseGCOverheadLimit重现了这段程序的目的,最后报错信息是Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

移除PermGen原因,见原文JEP 122: Remove the Permanent Generation

最终PermGen的方法区移至 Metaspace;字符串常量池移至 Java Heap。