前文我们针对垃圾收集的区域,垃圾收集的对象,垃圾收集算法进行了介绍;而垃圾收集器就是收集算法的实现,如下是HotSpot虚拟机常用的垃圾收集器的配合使用情况,连线表示可以配合使用:
垃圾收集器Serial收集器
Serial收集器是最基本的,历史最悠久的收集器,曾经在JDK1.3之前它是新生代唯一的选择,它采用的是我们前面提到的复制算法;从它的名字就可以看出单线程的收集器,只能通过单个收集线程完成收集工作,更重要的是在收集的过程中,其他线程必须停止工作,直到垃圾收集完成。在单CPU的情况下Serial收集器简单高效,避免了线程交互的开销,Serial收集器对于运行Client模式下的虚拟机是个很好的选择。
垃圾收集带来的停顿带来不良的用户体验,虚拟机设计者表示可以理解,但是也表示很委屈;例如你在收拾屋子时,也会尽量让孩子待在某处;而不是你一边收拾,孩子一边制造垃圾。
ParNew收集器
ParNew收集器是Serial收集器的多线程版本,同样的它也是复制算法的实现,重用了Serial收集器的大部分代码;不同的是它采用多条线程进行垃圾收集,同时提供了Serial收集器可用的控制参数,-XX:SurvivorRatio、-XX:PretenureSizeThreshold、XX:HandlePromotionFailure等;它是许多Server端虚拟机回收新生代的首选,其中一个与性能无关的原因是,除了Serial收集器,只有它能与CMS收集器配合使用。但是需要注意的是,在单CPU的情况下,ParNew收集器的效率绝对不会比Serial收集器更好,反而会因为线程之间的切换带来更多的开销。
Parallel收集器
Parallel收集器同ParNew垃圾收集器一样也是多线程对年轻代进行垃圾收集,但与其不同的是Parallel收集器更关注控制吞吐量,而其他垃圾收集器更关注停顿时间;吞吐量的含义是指运行用户线程的时间与虚拟机总运行时间的占比;例如虚拟机总共运行了100分钟,而垃圾收集耗费了1分钟,那么吞吐量则是99%;
Parallel收集器提供了两个参数用于控制吞吐量;-XX:MaxGCPauseMillis设置停顿时间,-XX:GCTimeRatio则是直接控制吞吐量;但是这里需要注意的是,有的人以为我将-XX:MaxGCPauseMillis设置的很小是不是就可以减少停顿时间了呢?其实不然,XX:MaxGCPauseMillis可以减少停顿时间,但有可能导致GC频率加快;比如原本10分钟进行一次GC,每次停顿100ms;有可能变为1分钟进行一次GC,每次停顿70ms,其效果可能可想而知。
Serial Old收集器
Serial Old收集器是Serial收集器针对老年代的版本,它使用的是标记-整理算法,同样的也是更加适用于Client模式下虚拟机,主要有两大用途,一种是跟JDK1.5版本之前的Parallel收集器配合使用,另一种用途是CMS收集器的后备预案,在Current Mode Failure时使用。
Parallel Old收集器
Parallel Old收集器是Parallel收集器的老年代版本,它采用的是标记-整理算法,在JDK1.6之前 Parallel 收集器只能与Serial Old收集器配合使用,在多CPU的环境中Serial Old无法利用多CPU的优势,直到Parallel Old收集器出现以后,关注“吞吐量”的收集器终于能够配合使用;在关注吞吐量的场景,以及CPU敏感的环境中可以选择 Parallel 跟ParallelOld 收集器配合使用。
CMS收集器
CMS(Current Mark Sweep)收集器是采用标记-清除算法,它用于老年代的垃圾收集,它是真正的并发的收集器,它可以实现“孩子一边扔垃圾,父母一边收拾”;它是如何实现的呢?它的收集过程分为以下四个步骤:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
初始标记是标记GC Roots 能直接关联的对象,而并发标记则是GC RootsTracing 的过程,至于重写标记则是用于重新标记并发标记过程中被用户线程修改的标记;所以初始标记,重新标记这两个过程任然是有停顿的。但是并发标记与并发清除两个阶段可以与用户线程一起工作,所以多被应用于B/S架构的服务端。
CMS收集器无法清理浮动垃圾,因为并发清除阶段同用户线程一同工作,所以可能有新的垃圾产生,所以可能导致“Current Mode Failure”,就需要我们使用前面提到的Serial Old 作为后备方案;除此之外,由于CMS收集器采用的是标记-清除算法,有可能产生我们我们之前在算法篇提到的不连续的内存空间,无法分配给需要较大的空间的对象,不得不触发新一次的Full GC。
G1收集器
G1(Garbage-First) 收集器,在JDK1.7正式商用,是当今最前沿的垃圾收集器之一,它采用的是复制算法与标记-整理算法的结合;同时其他收集器都收集的范围都是整个新生代或老年代,而G1则是将Java堆划分成多个大小相等的Region,虽然保留了新生代和老年代的概念,但它们不在是物理隔离的,都是一部分Region的集合。
G1相对于CMS收集器的另一个优势是可以建立可预测的时间模型;而它能够建立此模型的基础也正是由于便面了整个Java堆的垃圾收集,G1收集器会追踪每个Region垃圾收集的价值,建立一个优先列表,每次先回收最有价值的Region。
附录:垃圾收集器参数总结
参数 | 描述 |
---|---|
UseSerialGC | Client模式下虚拟机默认选择,打开后使用Serial+Serial Old组合使用 |
UseParNewGC | 打开后使用ParNew+Serial Old 组合使用 |
UseConcMarkSweepGC | 打开后使用ParNew+CMS+Serial Old 组合使用 |
UseParallelGC | 打开后使用Parallel+Serial Old 组合使用 |
UseParallelOldGC | 打开后使用Parallel+Parallel Old 组合使用 |
SurvivorRatio | 设置新生代中Eden与Survivor比例,默认是8,即Eden:Survivor=8:1 |
PretenureSizeThreShold | 设置直接晋升到老年代对象大小 |
MaxTenuringThreShold | 设置晋升到老年代年龄,没进行一次Minior GC 年龄加1 |
UseAdaptiveSizePolicy | 动态调整进入老年代的大小以及年龄 |
HandlePromotionFailure | 是否允许分配担保失败 |
ParallelGCThreads | 设置并行GC线程数 |
GCTimeRatio | GC占总时间比例 |
MaxGCPauseMillis | 设置GC最大停顿时间,仅在Parallel收集器使用时有效 |
CMSFullGCsBeforeCompaction | 设置在若干次FullGC之后,进行内存碎片整理,仅针对CMS有效 |
UseCMSCompactAtFullGC | 是否在FullGC之后进行内存碎片整理,仅针对CMS有效 |
CMSInitiatingOccupancyFraction | 设置老年代使用多少比例之后触发垃圾回收,默认68%,仅针对CMS有效 |
网友评论