写在前面:
前面两篇博客中介绍了GC的收集算法,和7种收集器的特点,而在这篇博客,我们会讨论什么情况下回触发一次Full GC。
分代收集补充:
在了解什么情况触发一次Full GC之前,我们先看一下JVM对象在内存区域中的年代划分。首先,我们知道GC收集器会根据回收对象的年龄,进行分代收集,如:Minor GC、Major GC。
- Minor GC:
新生代GC,指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生熄灭的特点,所以Minor GC十分频繁,回收速度也较快。
- Major GC:
老年代GC,指发生在老年代的垃圾收集动作,当出现Major GC时,一般也会伴有至少一次的Minor GC(并非绝对,例如Parallel Scavenge收集器会单独直接触发Major GC的机制)。Major GC的速度一般会比Minor GC慢十倍以上。
- 对象优先在Eden区分配:
HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1。大多数情况下,对象优先在Eden区中分配。当Eden区中没有足够空间进行分配时,将会触发一次Minor GC。
- 大对象直接进入老年代:
所谓的大对象是指,需要大量连续内存空间的Java对象。例如:很长的字符串或者数组。虚拟机提供了一个-XX:PretenureSizeThreshold参数。令大于这个-XX:PretenureSizeThreshold设置值的对象,直接在老年代分配。
- 长期存活的对象将进入老年代:
虚拟机为了分代收集,对每一个对象定义了一个对象年龄计数器(Age)。如果对象在Eden出生,并且经过一次Minor GC后,仍然存活并且能被Survivor区中每熬过一次Minor GC,,年龄就会增加1岁。当年龄增加到默认的15岁时,就会晋升到老年代。
晋升为老年代的阙值通过参数-XX:MaxTenuringThreshold设置。
Full GC的情况:
(1)调用Sytem.GC()
(2)老年代空间不足时
(3)GC担保失败:
在发生Minor GC之前,JVM会检查老年代最大可用的连续空间是否大于新生代所有对象总空间。如果条件成立,那么Minor GC是安全的。反之,如果不成立,那么要仍然要看HandlePromotionFailure值,是否允许担保失败。如果允许担保失败,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。如果大于,则冒险尝试一次Minor GC,如果小于或者不允许担保失败,则要进行一次Full GC。
补充:冒险尝试Minor GC,是因为新生代采用复制收集算法,每次使用一个Survivor空间进行轮换备份,因此,如果当大量对象在一次Minor GC之后,仍然存活,老年代就要进行分配担保。也就是老年代要保证本身有可以容纳这些对象的剩余空间。