上图为JVM运行时数据区。
1、方法区:(逻辑上)
是JVM的一个规范,所有虚拟机必须要遵守的。
是JVM所有线程共享的,主要用于存储类的信息、常量池、方法数据、方法代码等。
方法区逻辑上属于堆的一部分(下文会有提及),但是为了与堆区分,通常又叫非堆(Heap)区
2、堆
堆是JVM中线程共享的,在虚拟机启动的时候就已创建。
所有对象(对象本身)和数据都在堆中存放,也是GC的主要区域
3、PermGen(永久代)
PermGen , 就是 PermGen space ,全称是 Permanent Generation space ,是指内存的永久保存区域。
PermGen space 是JDK7及之前, HotSpot虚拟机基于JVM规范对方法区的一个落地实现。在JDK8被移除。
4、Metaspace(元空间、JDK8及之后):
元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
移除PermGen(永久代)从从JDK7 就开始。例如,字符串内部池,已经在JDK7 中从永久代中移除。直到JDK8 的发布将宣告 PermGen(永久代)的终结。
元空间则是JDK1.8及之后,HotSpot虚拟机对方法区的新实现。
但永久代仍存在于JDK7 中,并没完全移除,比如:
字面量 (interned strings)转移到 Java heap;
类的静态变量(class statics)转移到Java heap ;
符号引用(Symbols) 转移到 Native heap ;
知识拓展:
JDK版本 方法区的实现 运行时常量池所在的位置
JDK6 | PermGen space(永久代 | PermGen space(永久代) |
JDK7 | PermGen space(永久代 | Heap(堆) |
JDK8 | Metaspace(元空间 | Heap(堆) |
5、为什么使用元空间替换永久代?
通过上面分析,大家应该大致了解了 JVM 的内存划分,也清楚了 JDK8 中永久代 向 元空间的转换。不过大家应该都有一个疑问,就是为什么要做这个转换?所以,最后给大家总结以下几点原因:
表面上看是为了避免OOM异常。
因为通常使用PermSize和MaxPermSize设置永久代的大小就决定了永久代的上限,但是不是总能知道应该设置为多大合适, 如果使用默认值很容易遇到OOM错误。
当使用元空间时,可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。
更深层的原因还是要合并HotSpot和JRockit的代码,JRockit从来没有所谓的永久代,也不需要开发运维人员设置永久代的大小,但是运行良好。同时也不用担心运行性能问题了,在覆盖到的测试中, 程序启动和运行速度降低不超过1%,但是这点性能损失换来了更大的安全保障。
1)字符串存在永久代中,容易出现性能问题和内存溢出。
2)类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
4)Oracle 可能会将HotSpot 与 JRockit 合二为一。