要是没有实践过别人书本上的理论的话,就还是会说常量池在方法区里面,要是知道方法区已经随jdk升级,被逐步干掉的话,就会看到有的文章说移动到heap堆里面了,还有极少的说移动到Metaspace里面了,产生了分歧。这个时候就需要实践出真知了。
/**
* 测试 常量池在分区的位置
*
* @author LiXuekai on 2020/6/9
*/
public class StringConstantPoolTest {
public static void main(String[] args) {
List<String> list = Lists.newArrayList();
while (true) {
list.add(String.valueOf(System.currentTimeMillis()).intern());
}
}
}
代码很简单,那本书上也大致是这个样子。String.intern()会将字符串丢到字符串常量池里面。以此来不断增加常量池的使用部分。
jdk1.6的测试
他当时测试的时候,出的异常是方法区OOM.
当时的jdk还是1.6,我这就不测试1.6的了。
执行结果说明常量池在方法区。
jdk1.7的测试
下面是jdk1.7的测试情况,还是这个代码,就是启动参数设置的不一样。
使用的jvm参数设置:
-XX:+PrintGCDetails -Xms100M -Xmx100M -Xmn10M -XX:SurvivorRatio=8 -XX:PermSize=10m -XX:MaxPermSize=10m
然后这个程序的异常截图如下:
堆空间溢出。
使用jvm看内存分区的使用情况的截图:
可以看到堆里面old区总共90M,已经89M,这个是在报oom之前的截图。方法区总共就分了10m,在oom的时候,也就使用了7m多,说明这个常量池,在jdk1.7的时候,确实被安排到了堆Java heap里面了。
上面的说明加起来,使得下面这个理论得到了验证。
永久代主要存放以下数据:
JVM internal representation of classes and their metadata //类及其元数据的JVM内部表示
Class statics //类的静态
Interned strings //实际字符串,说的就是常量池吧
从 JDK7 开始,JDK 开发者们就有消灭永久代的打算了。有部分数据移到永久代之外了:
Symbols => native memory // 符号引用 >本机内存
Interned strings => Java Heap // Interned string => Java堆
Class statics => Java Heap //类statics => Java堆
jdk1.8的测试
使用的jvm参数设置:
-XX:+PrintGCDetails -Xms200M -Xmx200M -Xmn10M -XX:SurvivorRatio=8 -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:CompressedClassSpaceSize=5M
代码执行时候报的异常截图
使用jvm看内存分区的使用情况的截图:
设置堆的大小是200m,元空间就只有10m,测试了几次了,每次堆的old区里面也就残留81m,看元空间的最大是10m,使用了8.4m之后,差不多就oom了。堆还差得远呢。
说明这个常量池就是在元空间里面了吧,不能在Java heap里面了吧。
在看元空间的时候,有个这个图
说是开发jvm的人画的。元空间被分为2部分,类空间 class space 和非类空间 non class space
深入 Class Space:
最大的一部分是 Klass 结构,它是固定大小的。
然后紧跟着两个可变大小的 vtable 和 itable,前者由类中方法的数量决定,后者由这个类所实现接口的方法数量决定。
随后是一个 map,记录了类中引用的 Java 对象的地址,尽管该结构一般都很小,不过也是可变的。
vtable 和 itable 通常也很小,但是对于一些巨大的类,它们也可以很大,一个有 30000 个方法的类,vtable 的大小会达到 240k,如果类派生自一个拥有 30000 个方法的接口,也是同理。但是这些都是测试案例,除了自动生成代码,你从来不会看到这样的类。
深入 Non-Class Space
这个区域有很多的东西,下面这些占用了最多的空间:
- 常量池,可变大小;
-
每个成员方法的 metadata:ConstMethod 结构,包含了好几个可变大小的内部结构,如方法字节码、局部变量表、异常表、参数信息、方法签名等;
最后,那就按照老外的说法来吧,我的测试也稍微辅助说明一下吧,虽然代码里面报错是Java heap 溢出。这点奇怪了。
姑且最终结论,常量池就是在Metaspace 元空间里面。
参考:
来源:oschina
链接:https://my.oschina.net/u/4383725/blog/4313144