12_年轻代和老年代垃圾回收机制
约 1398 字大约 5 分钟
2024-08-10
躲过 15 次 GC 之后进入老年代
一开始创建的对象是分配在新生代里的,每一次 Young GC 过后,存活的对象年龄就会增长一岁
默认设置下,当对象的年龄到达 15 岁的时候,也就是躲过 15 次 GC,他就会被转移到老年代里去
具体躲过多少次 GC 进入老年代,可以通过 -XX:MaxTenuringThreshold 参数来设置
动态对象年龄判断
除了对象年龄外还有一个规则可以让对象进入老年代,不用等待 15 次 GC 过后
大致规则是,当前存放对象的 Survivor 区中,一批对象的总大小大于了这块 Survivor 区内存的 50%,那么此时大于等于这批对象年龄的对象,就可以直接进入老年代了
举个例子,假如 Survivor 区内存大小为 100MB,此时这个 Survivor 区有两个年龄一样大的对象,这两个对象加起来大小超过了 50MB(超过了 Survivor 区一半大小),这个时候,Survivor 区中大于等于 2 岁的对象就要全部进入老年代里去了
这就是所谓的动态年龄判断规则,这个规则会让一些新生代的对象进入老年代
这个规则运行的时候实际逻辑:年龄1 + 年龄2 + 年龄3 + 年龄n 的对象总大小超过了 Survivor 区的 50%,此时就会把年龄 n 及以上的对象都放入老年代
无论是躲过
15次GC还是动态年龄判断规则,都是希望那些可能是长期存活的对象,尽早进入老年代
大对象直接进入老年代
JVM -XX:PretenureSizeThreshold 参数设置大于多少的是大对象,这里值为字节数,比如 1048576 字节就是 1MB
创建对象时,对象的大小大于了这个值,此时就直接把这个大对象放到老年代里去,不会经过新生代
之所以这么做是为了避免新生代出现那种大对象,然后屡次躲过 GC,还得把这个大对象在两个 Survivor 区里来回复制多次才进入老年代
这么大的对象在内存里来回复制是比较耗费时间的
Young GC 后 Survivor 放不下存活对象,直接进入老年代
当发生 Young GC 时,Eden 区里有 150MB 存活对象,Survivor 区是放不下的,这个时候必须得把这些对象直接转移到老年代去,如下图所示

老年代空间分配担保规则
如果新生代有大量对象存活下来,Survivor 区也放不下,必须转移到老年代去,如果老年代空间也放不下该怎么办呢?这就是下面要介绍的老年代空间分配担保规则了
1、每次
Young GC之前,JVM会检查一下老年代可用内存空间是否大于新生代所有对象总大小(极端情况下所有对象都存活下来,都进入老年代)2、老年代可用内存大小大于新生代所有对象总大小,此时放心的对新生代发起一次
Young GC3、老年代可用内存大小小于新生代所有对象总大小,看是否配置了
-XX:-HandlePromotionFailure(JDK1.6以后废弃了,默认开启) 参数是否设置了(1)参数未设置,直接触发
Full GC,就是对老年代进行垃圾回收,尽量腾出一些内存空间,然后再执行Young GC(2)参数设置了,会看老年代的可用内存大小,是否大于之前每次
Young GC后进入老年代对象的平均大小(比如之前每次Young GC后,平均有10MB的对象进入老年代,此时老年代可用内存大小大于10MB,说明很有可能这次Young GC之后也是差不多10MB的对象进入老年代,此时老年代空间是够的)① 经过判断,老年代可用内存小于之前每次
Young GC后进入老年代对象的平均大小,直接触发Full GC② 经过判断,老年代可用内存大于之前每次
Young GC后进入老年代对象的平均大小,冒险尝试Young GCⅠ
Young GC过后,剩余存活对象大小小于Survivor区的大小,存活对象进入SurvivorⅡ
Young GC过后,剩余存活对象大小大于Survivor区的大小,小于老年代可用内存大小,存活对象进入老年代Ⅲ
Young GC过后,剩余存活对象大小既大于Survivor区的大小,也大于老年代可用内存大小,就会发生Handle Promotion Failure的情况,触发一次Full GC
(3)
Full GC是对老年代进行垃圾回收,同时一般也会对新生代进行垃圾回收。如果Full GC之后,老年代还是没有足够空间存放Young GC过后的存活对象,此时就会OOM内存溢出
一般老年代触发垃圾回收的情况
1、
Young GC之前,各种检查后发现Young GC之后进入老年代对象太多,老年代也放不下,提前触发Full GC之后再进行Young GC2、
Young GC之后,剩余存活对象太多老年代放不下