- 首先明确:只有HotSpot才有永久代。 BEA JRockit、IBM J9等来说,是不存在永久代的概念的。原则上如何实现方法区属于虛拟机实现细节,不受《Java虚拟机规范》管束,并不要求统一。
- Hotspot中 方法区的变化:
- jdk1.6及之前:有永久代(permanent generation) ,静态变量存放在 永久代上
- jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中
- jdk1.8及之后: 无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆
)
永久代为什么要被元空间替换
- 随着Java8的到来,HotSpot VM中再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫做元空间( Metaspace )。
- 由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。
- 这项改动是很有必要的,原因有:
- 1)为永久代设置空间大小是很难确定的。 在某些场景下,如果动态加载类过多,容易产生Perm区的O0M。比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。
"Exception in thread' dubbo client x.x connector’java.lang.OutOfMemoryError: PermGenspace"
,而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。 - 2)对永久代进行调优是很困难的。
StringTable 为什么要调整
jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才会触发。而full GC 是老年代的空间不足、永久代不足时才会触发。这就导致了StringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存.
- 1)为永久代设置空间大小是很难确定的。 在某些场景下,如果动态加载类过多,容易产生Perm区的O0M。比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。
如何证明静态变量存在哪
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /** * 《深入理解Java虚拟机》中的案例: * staticObj、instanceObj、localObj存放在哪里? */ public class StaticObjTest { static class Test { static ObjectHolder staticObj = new ObjectHolder(); ObjectHolder instanceObj = new ObjectHolder(); void foo() { ObjectHolder localObj = new ObjectHolder(); System.out.println("done"); } } private static class ObjectHolder { } public static void main(String[] args) { Test test = new StaticObjTest.Test(); test.foo(); } } |
-
staticObj随着Test的类型信息存放在方法区,instance0bj 随着Test的对象实例存放在Java堆,localobject则是存放在foo()方法栈帧的局部变量表中。
Java
1 2 3 4
hsdb>scanoops 0x00007f32c7800000 0x00007f32c7b50000 JHSDB_ _TestCase$Obj ectHolder 0x00007f32c7a7c458 JHSDB_ TestCase$Obj ectHolder 0x00007f32c7a7c480 JHSDB_ TestCase$Obj ectHolder 0x00007f32c7a7c490 JHSDB_ TestCase$Obj ectHolder
-
测试发现:三个对象的数据在内存中的地址都落在Eden区范围内,所以结论:只要是对象实例必然会在Java堆中分配。
-
接着,找到了一个引用该staticObj对象的地方,是在一个java. lang . Class的实例里,并且给出了这个实例的地址,通过Inspector查看该对象实例,可以清楚看到这确实是一个 java.lang.Class类型的对象实例,里面有一个名为staticObj的实例字段:
-
从《Java 虛拟机规范》所定义的概念模型来看,所有 C1ass 相关的信息都应该存放在方法区之中,但方法区该如何实现,《Java 虚拟机规范》并未做出规定,这就成了一件允许不同虚拟机自己灵活把握的事情。JDK7 及其以后版本的 Hotspot 虚拟机选择把静态变量与类型在 Java 语言一端的映射 C1ass 对象存放在一起,存储于】ava 堆之中,从我们的实验中也明确验证了这一点.