了解了Java虚拟机垃圾回收算法一文中的内容,我们来学习它们的具体是实现——垃圾回收器。
不用的厂商、不同版本的虚拟机都会提供不同的垃圾回收器
,用户可以根据自己的场景需求配置要使用的垃圾回收器。
学习垃圾回收器,需要明确一个思想:没有最好的垃圾回收器,只有最合适的
。
了解垃圾回收器只为根据具体的应用场景选择最合适的那一个。
Serial
Serial回收器是采用复制算法
的新生代
回收器。
Serial顾名思义,是一个单线程
的回收器。
所谓单线程,指的是在垃圾回收阶段,只使用一个线程去完成。同时回收过程会导致STW(“Stop The Word”)
。(暂停其他所有线程,表现为不对外提供服务)。
STW带来的糟糕的用户体验,一直是虚拟机开发者努力解决的问题。可以说后续的回收器都在致力于解决这个问题。
Serial的单线程作业,在当前多CPU的环境下显得不是很高效。但是单CPU环境下
,Serial相比于其他回收器就会因为它没有线程切换开销而更加高效。
参数
-XX:+UseSerialGC:添加该参数使用Serial进行垃圾回收。
ParNew
ParNew,Par表示Parallel并行
,即使用多线程进行垃圾回收,New表示新生代
。
ParNew是Serial的多线程版本
,除使用多线程外,其余与Serial一样,也是使用复制算法,同时实现了也共用了很多代码。
参数
-XX:+UseParNewGC:使用ParNew进行垃圾回收。
-XX:ParallelGCThreads:指定线程数量。
Parallel Scavenge
Parallel Scavenge,新生代回收器,使用并行多线程
和复制算法
回收。
相比于其他回收器,Parallel Scavenge关注于吞吐量
(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾回收时间)),
其他回收器关注的是在垃圾回收阶段的用户线程的停顿时间
。
所以Parallel Scavenge更加适用于后台计算类
的场景,其他回收器适用于与用户交互的场景。
参数
-XX:MaxGCPauseMillis:最大垃圾回收停顿时间。设置过小,会导致GC频繁而降低吞吐量。
-XX:GCTimeRatio:垃圾回收时间占比,通过1/(1+N)计算。
-XX:+UseAdptiveSizePolicy:GC自适应调节策略,开启后不需要手动设置细节参数
Serial Old
Serial的老年代版本
,单线程回收,采用“标记-整理”
算法。
主要用于虚拟机的Client模式下。
在Server模式下,与Parallel Scavenge搭配使用或者作为CMS回收器在发生Concurrent Mode Failure时的后备预案
。
Parallel Old
Parallel Scavenge 的老年代版本
,使用“标记-整理”
算法。
Parallel Old 的出现取代了 Parallel Scavenge+Serial Old的组合,Parallel Scavenge+Parallel Old 的组合更大程度的利用的多CPU的硬件环境。
参数
-XX:+UseParallelOldGC:使用ParallelOld回收器;
CMS
CMS(Concurrent Mark Sweep)并发标记清理回收器,目标是获取最短回收停顿时间
。适用于与用户交互高频的场景。
老年代回收器,使用“标记-清除”
算法。
所谓并发,指的是垃圾回收线程与用户线程同时执行(可能交替执行,可能处于不同CPU上运行)
。
CMS的并发,并不代表着不会导致STW。CMS分为4个步骤:初始标记,并发标记,重新标记,并发清除,其中的初始标记和重新标记仍要STW
。
初始标记:STW,标记GC Roots能关联的对象。
并发标记:进行GC Roots Tracing过程。用户程序在运行,不保证能标记所有对象。
重新标记:STW,修正并发标记期间因程序运行到导致变动的标记对象。
并发清理:回收所有死亡对象。
不足
• CPU敏感型。并发会占用CPU资源,导致程序变慢,而CPU的数量会进一步影响到垃圾的回收与程序的运行。
• 浮动垃圾无法处理:浮动垃圾即并发清理阶段产生的垃圾,需要等到下一次GC进行清理。
• 可能出现Concurrent Mode Failure:浮动垃圾的存在,需要预留空间给程序运行。而当预留空间不足时,就会出现Concurrent Mode Failure
。 这时会启用Serial Old,而导致FullGC。
• 内存碎片的产生:“标记-清理”算法产生了内存碎片,会在分配大对象时,导致FullGC提前。
参数
-XX:+UseConcMarkSweepGC:使用CMS收集器,使用该参数后会默认使用ParNew作为新生代回收器。 -XX:+UseCMSCompactAtFullCollection:在进行FullGC时,开启内存碎片整理。 -XX:+CMSFullGCsBeforeCompaction:执行一定次数的内存不整理的FullGC后,下一次FullGC会进行内存整理。 -XX:CMSInitiatingoccupanyFraction:内存使用率的阈值,达到阈值就进行GC。 -XX:ParallelCMSThreads:CMS线程数量
G1
G1将Java堆划分为多个大小相等的region
,并在后台为每个region的价值大小
维护一个优先列表
。
每次回收时,根据允许的回收时间,回收价值最大的region(garbage-first的由来)
。
• G1不在需要与其他回收器结合
来管理整个堆。
• G1在概念上还保留着新生代与老年代的概念
,但是在物理上已经不存在,它们只是不同region的集合。
• 由于回收管理的不在是整个堆,而是部分region,可以预测回收需要的时间
。
• G1在整体上基于“标记-整理”算法,在region间基于“复制”算法。不会存在内存碎片
。
• 并发与并行
,充分利用多核多CPU环境,来降低STW时间。
初始标记:STW,标记GC Roots能关联的对象。
并发标记:进行GC Roots Tracing过程,找出存活对象。
最终标记:STW,修正并发标记期间因程序运行到导致变动的标记对象。
筛选回收:对各个region的价值进行排序,根据用户期望的GC停顿时间进行回收。
参数
-XX:+UseG1GC:使用G1收集器
-XX:InitiatingHeapOccupancyPercent:堆的占用率,当达标时,开始并发标记。
-XX:MaxGCPauseMillis:最大的停顿时间。
-XX:G1HeapRegionSize:设置region大小。