一、为什么新生代的垃圾回收快?

因为新生代的存活对象比较少,通常占10%左右,从GC Roots出发追踪的存活对象比较少,速度比较快,清理时,只需要把存活的对象移动到Survivor区,原来的Eden区和另外一个Survivor区的所有垃圾对象全部快速删除即可。

二、为什么老年代的垃圾回收慢?

因为老年代的存活对象比较多,从GC Roots出发追踪的存活对象比较多,速度比较慢,比如老年代常用的垃圾回收器CMS,就用了三个步骤来进行标记操作(初始标记、并发标记、 重新标记),在清理阶段,由于存活对象比较多,清理之后内存碎片比较多,那么在清理之后会进行整理。如果在清理阶段,剩余的内存空间不足以放入老年代对象,那么引发“Concurrent Mode Failure”问题,那么JVM将采用Serial Old垃圾回收器重新执行垃圾回收,Serial Old采用串行的方式执行垃圾回收,效率比CMS更慢,综上,老年代的垃圾回收通常比较慢。

三、如何避免频繁触发Full GC?

我们知道老年代垃圾收回比较耗时,通常比新生代垃圾回收慢很多倍,那么如何避免频繁触发Full GC呢?
我们知道对象存入老年代有以下几个条件:
(1)存活对象年龄达到15岁(-XX:MaxTenuringThreshold)。
(2)新生代垃圾回收之后,Survivor区对象大小占用了50%以上空间。
(3)大对象(1MB以上)直接进入老年代(-XX:PretenureSizeThreshold)。

那么避免对象进入老年代可以进而避免频繁触发Full GC,有如下方式:
(1)调大对象进入老年代的年龄,比如从15岁调整到30岁。
(2)调整新生代和老年代占比,从默认的[新生代:老年代=1:2],调整为[新生代:老年代=2:1]。调整Eden区和Survivor区的占比,从默认的[Eden:Survivor=8:2],调整为[Eden:Survivor=6:4]
(3)调大大对象的定义,比如从1MB调整到10MB。

结合以上几种方式,可以大幅度避免频繁Full GC。

打赏
支付宝 微信
上一篇 下一篇