JVM垃圾回收——新生代,老年代,永久代,Minor GC,Full GC

参考资料:

  1. 《深入理解Java虚拟机》——周志明
  2. https://blog.csdn.net/u010796790/article/details/52213708
  3. https://blog.csdn.net/lirenzuo/article/details/77749925
  4. https://www.cnblogs.com/ygj0930/p/6522828.html

周志明先生,在《深入理解Java虚拟机》一书中曾提到:程序计数器,虚拟机栈,本地方法栈这3个区域是线程私有的,随线程而生,随线程而灭,这几个区域就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存就自然跟着回收了。主要讨论Java堆和方法区的回收。(Java堆和方法区是《Java虚拟机规范》的术语)

Java堆通常分为新生代,老年代。永久代,实际上就是方法区。

【方法区】是JVM的一种规范,存放类信息,常量,静态变量,即时编译后的代码等。

【永久代】是HotSpot的一种具体实现,实际上指的就是方法区,或者说用永久代来实现方法区。对于其他虚拟机来说是不存在永久代的概念的。

新生代:

对于新生代而言,无论是使用哪一种垃圾收集器(JDK1.6之前新生代的垃圾收集器有三种:Serial,ParNew,Parallel Scavenge)采用的都是复制收集算法

JDK1.6中,新生代与老年代的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),新生代又可细分为:EdenFrom SurvivorTo Survivor三个区(811),也就是说新生代中可用内存空间为整个新生代容量的90%,有10%的内存被浪费。

Eden:新对象的出生地。
From Survivor上一次GC的幸存者,作为这一次GC的被扫描者。
To Survivor保留MinorGC过程中的幸存者。

MinorGC的触发条件】Eden区内存不足的时候,虚拟机将进行一次MinorGCSurvivor区内存不足不会触发MinorGCMinorGC之后,可能会与一些新生代的对象年龄满足进入老年代,老年代的占用会有所升高。

MinorGC的过程MinorGC采用复制算法。首先,把EdenFrom Survivor区域中存活的对象复制到To Survivor区域,同时把这些对象的年龄+1(默认情况下15岁就直接送到老年代了,晋升老年代的阈值可以通过-XX:MaxTenuringThreshold设置);然后,清空EdenFrom Survivor中的对象;最后,To SurvivorFrom Survivor互换,原To Survivor成为下一次GC时的From Survivor区。

扫描二维码关注公众号,回复: 5639560 查看本文章

老年代:

主要存放程序中年龄较大和需要占用大量连续内存空间的对象。老年代的对象比较稳定,所以MajorGC/Full GC执行的频率较低。一般都是在空间不足的时候才会执行MajorGC/Full GC。

JDK1.6之前老年代的垃圾收集器Serial Old,Parallel Old采用的是标记—整理算法,CMS采用的标记—清除算法。标记—清除算法会产生大量的内存碎片。

【新生代的对象进入老年代的情况】:

  1. 设置了-XX:PretenureSizeThreshold3M 参数,那么大于3M的对象就会直接就进入老年代,这样做的目的是避免在Eden区以及两个Survivor区之间发生大量的内存复制。(只对于Serial收集器和ParNew两款收集器有效)
  2. 长期存活的对象将进入老年代:对于在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度,就会晋升到老年代中。(默认15岁,可以通过-XX:MaxTenuringThreshold设置)
  3. 如果在Survivor空间中相同年龄所有对象对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold要求的年龄。
  4. 空间分配担保机制:当Minor GC时,新生代存活的对象大于Survivor的大小时,这时一个Survivor装不下它们,那么它们就会进入老年代。

永久代:

HotSpot中对于JVM规范中方法区的实现,指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存

Full GC:

         关于Major GC,从周志明先生在《深入理解Java虚拟机》中P92可以看出,Major GC和Full GC通常情况下应该是等价的。主要是发生在老年代GC,出现Major GC/Full GC,通常情况下伴随着至少一次的Minor GC(并不是绝对的),Major GC/Full GC的速度一般比Minor GC慢10倍以上。

【Full GC的触发条件】:

  1. 当准备要触发一次Minor GC时,如果发现统计数据说之前Minor GC的平均晋升大小比目前老年代剩余的空间大,则不会触发Minor GC而是转为触发Full GC(因为HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集老年代的GC都会同时收集整个GC堆,包括新时代,所以不需要事先触发一次单独的Minor GC);
  2. 如果有永久代的话,要在永久代分配空间但已经没有足够空间时,也要触发一次Full GC;(Java虚拟规范中并不要求虚拟机在方法区实现垃圾收集,而且在方法区中进行垃圾收集的“性价比”较低。在大量使用频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。)
  3. System.gc()、heap dump带GC,默认也是触发Full GC。

猜你喜欢

转载自blog.csdn.net/ChenjCarryOn/article/details/81807741