现在先学一下垃圾回收器的种类以及如何使用垃圾回收器。之后实战篇的时候再学习垃圾回收器的原理。
垃圾回收器是垃圾回收算法的具体实现。
HotSpot之中垃圾回收器分为年轻代和老年代,年轻代和老年代有着不同的垃圾回收算法实现,除了最特殊的G1垃圾回收器以外,其他垃圾回收器都必须两两配对使用(年轻代和老年代)。
(1)年轻代垃圾回收器
与老年代中的SerialOld回收器搭配使用。
与老年代的CMS回收器搭配使用。
Parallel Scavenge垃圾回收器
这个回收器可以保证系统的吞吐量(吞吐量指用户线程执行时间占总执行时间的比例。比如吞吐量99,说明用户执行时间占99%,垃圾回收的时间占1%)。吞吐量过大,无法保证垃圾被及时清理,等到垃圾积累了一定数量后,再来清理会使得STW暂停时间过长。
它的老年代搭配是Parallel Old垃圾回收器。这个垃圾回收器还是JDK8默认的垃圾回收器。
? 不过PS垃圾回收器这个GC比较有意思,它还提供了一个参数可以设置最大的STW时间。
? 最大暂停时间和吞吐量是矛盾的。想要小的最大暂停时间,吞吐量就不可能大;想要大的吞吐量,最大暂停时间就不可能小(上边分析过了)。如果同时设置了最大暂停时间和吞吐量,需要多做测试,尽量把这两个值调整到最合适的状态。
? 值得注意的是,减小最大暂停时间是通过减小堆内存来实现的。垃圾回收器会根据最大暂停时间动态调整堆的大小。
(2)老年代垃圾回收器
Serial Old垃圾回收器
与年轻代的Serial垃圾回收器搭配使用。
CMS垃圾回收器
与年轻代的ParNew回收器搭配使用。
Parallel Old垃圾回收器
这个垃圾回收器的年轻代搭配是Parallel Scavenge垃圾回收器。与它的年轻代搭档一样,它的暂停时间也比较长。
(3)G1垃圾回收器
jdk9之后默认的垃圾回收器是G1(Garbage First)垃圾回收器。
G1垃圾回收器可以说整合了jdk8之前的垃圾回收器的优点,jdk9之后强烈推荐使用G1垃圾回收器。
G1之外的垃圾回收器,内存一般都是连续的。
G1的内存结构不再是连续的了。
G1垃圾回收有两种方式:
年轻代回收(Young GC)
回收Eden区和Survivor区垃圾对象。通过参数-XX:MaxGCPauseMills=n
(n默认是200)设置每次垃圾回收时的最大暂停时间毫秒数。G1垃圾回收器会尽可能保证暂停时间。
执行流程:
G1在Young GC时,会记录每个Region的平均耗时,作为下次回收的参考依据。比如,最大暂停时间设置成100ms,每个Region回收耗时30ms,下次就只能回收3个Region了。
某些对象如果过大(大小超过Region的一半),不会放入年轻代中,而是放入老年代中的Humongous区(Humongous:巨大的)。如果对象大小超过一个Region的大小,它有可能会横跨多个Region。
之所以不会产生内存碎片是因为回收时会将多个Region中的存活对象复制到新的Region区中,然后将没用的Region一口气清空。
混合回收(Mixed GC)
经过Young GC多次回收,有很多对象晋升老年代。如果年轻代和老年代空间不够用了(或者说总堆占有率超过参数-XX:InitiatingHeapOccupancyPercent
(默认45%)),就会触发混合回收Mixed GC,回收年轻代中所有Region的垃圾对象和老年代中部分Region的垃圾对象以及老年代中Humongous区中的所有对象。采用复制算法来完成。复制算法保证了老年代中也不会出现内存碎片。
值得注意的是:Young GC和Mixed GC中发现没有足够的空Region可以存放存活的对象,会出现Full GC(单线程执行标记-整理算法清理掉非存活对象,获取空的Region)。Full GC耗费的时间会比较久,所以尽量让堆中有空余的内存。