老年代的垃圾回收

什么情况下对象会进入到老年代

在JVM中,每个对象都有一个GC年龄,当发生GC时,如果对象没有被回收,那么它的GC年龄就+1

  • 默认情况下,GC年龄超过15的对象会进入老年代,如果想自定义这个年龄可以通过JVM参数-XX:MaxTenuringThreshold来设置
  • 动态年龄判断,如果在Survivor区域中,同一年龄段的对象占用内存比例达到50%,那么所有年龄大于等于该年龄的对象都会进入老年代
  • 大对象直接进入老年代,通过JVM参数-XX:PretenureSizeThreshold来设置多大的对象可以直接进入老年代,该做法就是为了避免大对象在新生代中来回复制导致系统消耗增加
  • GC后,剩余的存活对象超过了Survivor区域的大小,那么这些对象也会直接进入老年代

老年代空间分配担保机制

如果新生代有大量对象存活,同时Survivor区也放不下了,那么这些对象必须要放到老年代中,但是如果此时老年代中剩余空间也不足以放下这些对象,该怎么办?

  • 首先,在每一次执行Minor GC前,JVM都会先检查一下老年代剩余可用空间是否大于新生代所有对象的大小总和,防止极端情况下所有的对象都存活下来了

  • 如果发现老年代剩余空间大于新生代所有对象大小总和,那么就可以执行Minor GC了

  • 如果老年代剩余空间小于新生代所有对象大小总和,那么可能会出现新生代所有对象都存活下来都需要转移到老年代中,但是老年代空间不足的情况,此时JVM会检查是否设置了-XX:-HandlePromotionFailure参数

  • 如果设置了上面的参数,那么JVM就会继续判断,老年代当前内存大小是否大于之前每一次Minor GC后进入老年代的对象平均大小并且当前老年代剩余内存大于这个平均大小,那么这次Minor GC后很可能是能够放的下这么多对象的

  • 如果上面判断空间不足,或者未设置-XX:-HandlePromotionFailure该参数,那么就会直接触发老年代的Full GC,回收掉一些没有被引用的对象然后再执行Minor GC

  • 如果上面判断空间足够,那么会进行Minor GC,会有几种情况发生

    1. Minor GC后,剩余存活对象的大小小于Survivor区的大小,那么存活对象直接进入Survivor区
    2. Minor GC后,剩余存活对象的大小大于Survivor区的大小,但是小于老年代可用空间大小,那么存活对象直接进入老年代
    3. 如果以上都不满足,那么就会触发Full GC
    4. 如果Full GC后,内存还是不足,那么就会发生OOM

Full GC的同时一般都会带着Minor GC,因为Full GC是为了Minor GC后存活的对象能够进入老年代

老年代采用的垃圾回收算法 - 标记清理 + 定时整理

  • 不会出现内存碎片
  • 但是该算法效率较低,因此老年代中如果频繁发生Full GC,会严重影响系统性能

什么情况下会触发老年代的垃圾回收

  • MinorGC前,如果当前老年代的剩余空间小于新生代的对象总大小,并且没有设置空间分配担保JVM参数,直接触发Full GC
  • 在空间分配担保机制进行检查时,如果当前老年代剩余空间小于之前平均每次MinorGC后进入老年代的对象总大小时,触发Full GC
  • 当Minor GC后,Survivor区中无法存放剩余存活对象,并且此时老年代剩余空间也不足以存放这些对象时,触发Full GC
  • 老年代存活对象空间占比达到了JVM参数“-XX:CMSInitiatingOccupancyFaction设置的阈值(默认为92%),触发Full GC