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 GC
3、老年代可用内存大小小于新生代所有对象总大小,看是否配置了
-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 GC
2、
Young GC
之后,剩余存活对象太多老年代放不下