天天看点

java jdk1.7常量池移到哪去了?

今天模拟了一下常量池的oom,突然发现设置的参数-XX:PermSize=10M -XX:MaxPermSize=10M不管用了,同时发现内存一直在上升,当上升到一个极值就会趋于平稳,然后再过一段时间会报:

Exception in thread “main” java.lang.OutOfMemoryError: GC overhead limit exceeded

这个异常是当GC的时间超过总运行时间的98%才会报的,是为了防止GC占用的时间过长。

模拟常量池源码:

public static void main(String[] args) throws Throwable {
        List<String> list = new ArrayList<String>();
        int i=;
        while(true){
            list.add(String.valueOf(i++).intern());
        }

    }
           

那么问题来了,我们从前知道的是常量池是放在Java JVM中的方法区中的,许多人也叫它“永久代”,可以通过-XX:PermSize=20M -XX:MaxPermSize=20M来设置大小,当这个区域内存溢出会报:

Exception in thread “main” java.lang.OutOfMemoryError:PermGen space的内存溢出异常,但是这里却不是那么一回事,反而GC的时间会过长,那么我猜测这个常量池是移动到了java堆中去了,下面测试一下:

首先,源代码不变,增加运行时JVM的参数:

-Xmx20m -Xms20m -XX:-UseGCOverheadLimit,这里的-XX:-UseGCOverheadLimit是关闭GC占用时间过长时会报的异常,然后限制堆的大小,运行程序,果然,一会后报异常:

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

从上面的异常可以知道我们测试增加的常量都放到了堆中,所以限制堆内存以后,不断增加常量,堆内存会溢出。和我们猜测的一样,同时我也查看了官网发现了官网有说明http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html:

Area: HotSpot

Synopsis: In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences.

RFE: 6962931

通过自己的实践和官网说明,可以知道java jdk1.7中的常量池确实是移到了堆中,同时在jdk1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域,如果想了解更多可以参考:

http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html

http://blog.csdn.net/zhyhang/article/details/17246223/