JVM堆——新生代,老年代,永久代,GC

JVM中的堆一般分为新生代,老年代,永久代。

新生代

存放新生的对象,占据堆的1/3空间,MinorGC(采用复制清除算法)进行垃圾回收。
新生代分 Eden、ServivorFrom、ServivorTo三个区。
Eden:新对象的出生地。当Eden区内存不足的时候,虚拟机将进行一次MinorGC。
ServivorTo:保留MinorGC过程中的幸存者。
ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。
MinorGC的过程:MinorGC采用复制算法。首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域,同时把这些对象的年龄+1(默认情况下15岁就直接送到老年代了);然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

老年代

主要存放程序中年龄较大和需要占用大量连续内存空间的对象。
老年代的对象比较稳定,所以MajorGC执行的频率较低。一般都是在空间不足的时候才会执行MajorGC。
MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
当老年代装不下的时候,就会抛出Out of Memory异常。

永久代

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出Out of Memory异常。
在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制.
采用元空间而不用永久代的几点原因:
1、为了解决永久代的Out of Memory问题,元数据和class对象存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出(因为堆空间有限,此消彼长)。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。

猜你喜欢

转载自blog.csdn.net/hotchange/article/details/79761845