垃圾收集器就是垃圾收集算法的具体实现,下面是垃圾收集器的汇总
Serial和Serial Old收集器
Serial收集器是最基本、最早的收集器,Serial收集器是单一线程,就是在GC的时候STW(Stop The World),
暂停所有用户线程,如果GC时间过长,用户可以感到卡顿。Serial Old也是单线程,作用于老年代。

优点:简单高效,拥有很高的单线程收集效率
缺点:需要STW,暂停所有用户线程
算法:Serial采用复制算法,Serial Old采用标记-整理算法
适用场景:Client 模式(桌面应用);单核服务器。
ParNew收集器
ParNew是Serial的多线程版本,实现了并行收集,原理跟Serial一致
并行指的是多个GC线程并行,但是用户线程还是暂停,并发指的是用户线程和GC线程同时执行。
ParNew默认开启和CPU个数相同的线程数进行回收。
优点:在多CPU时,比Serial的效率高。
缺点:还是需要STW,单CPU时比Serial效率低
算法:复制算法
适用场景:多核服务器;与 CMS 收集器搭配使用
Parallel Scavenge收集器
新生代收集器,也是复制算法,和ParNew一样并行的多线程收集器,更关注系统的吞吐量(吞吐量=(运行用户代码的时间)/(运行用户代码的时间+GC时间)),
Parallel Scavange提供了两个参数用于精确控制吞吐量:
-XX:MaxGCPauseMillis //GC最大停顿毫秒数,必须大于0-XX:GCTimeRation //设置吞吐量大小,大于0小于100,默认值为99
是否把MaxGCPauseMillis设置小点就会让GC速度变快?答案是否定的,如果设置时间过小,Parallel Scavange会牺牲吞吐量和新生代空间来交换,比如新生代400Mb需要GC时间为100ms,设置成50ms了,那么就会把新生代调小为200Mb,这样肯定时间就降下来了,然而这种操作可能会降低吞吐量,原先10s触发一次GC,每次100ms,修改时间后变成5s触发一次GC,每次70ms,那么10ms触发两次GC的时间变成了140ms,吞吐量反而降低。 可以通过参数-XX:+UseAdaptiveSizePolicy开启自适应策略,这样我们不需要手动设置,虚拟机会根据运行情况动态调整。
Parallel Old收集器
是Parallel Scavange的老年代版本,因为Parallel Scavange无法和CMS搭配使用,所以只能和Serial Old。自从Parallel Old出现,就有了Parallel Scavange+Parallel Old的组合,这是JDK1.8使用的,注重吞吐量的一组收集器
优点:可以精确控制吞吐量。
缺点:还是需要STW
算法:标记-整理算法
适用场景:注重吞吐量,高效利用 CPU,需要高效运算且不需要太多交互
CMS收集器
这是优化GC停顿时间为目标的收集器,并发回收(仍然需要STW,但是时间很短)。
通过-XX:+UseMarkSweepGC启用。CMS基于标记-清除算法实现。整个过程分为四步:
- 初始标记:需要STW,标记GC Roots对象。
并发标记:这个阶段可以和用户线程一起进行,分为三步:
(1)根据第一步找到的GC Roots开始搜索跟GC Roots相连的对象。 (2)预清理:这个阶段是为了处理并发标记之后发生变化的对象。 (3)可被终止的预清理:这个阶段会有一个abort触发条件,该阶段存在的目的是希望能发生一次Young GC,这样就可以减少Young区对象数量,降低重新标记的工作量,因为重新标记会扫描整个堆内空间,可以通过参数-XX:+CMSScavangeBeforeRemark控制在重新标记前发生一次Young GC,默认为false。
重新标记:需要STW,这个阶段是为了修正在阶段2标记之后产生变化的对象。
- 并发清除:和用户线程同时进行,开始正式清理垃圾,此阶段产生的垃圾留待下次清除。

优点:并发收集,低停顿
缺点:产生大量碎片,并发阶段会降低吞吐量
算法:标记清除
适用场景:重视服务器响应速度,要求系统停顿时间最短
G1
G1是以优化GC停顿时间为目标的收集器,它尝试以高概率满足GC停顿时间为目标,同时实现高吞吐量。现在已经成为 jdk9 默认的收集器。
前面几款收集器收集的范围都是新生代或者老年代,G1 进行垃圾收集的范围是整个堆内存,
它采用 “ 化整为零 ” 的思路,把整个堆内存划分为多个大小相等的独立区域(Region),
在 G1 收集器中还保留着新生代和老年代的概念,它们分别都是一部分 Region,如下图:
上图智能柜被划分成一组大小相同的Region,每个Region都是连续的虚拟内存范围,G1可以知道哪个Region区域内大部分是空的,这样就可以在每次允许的收集时间内优先回收价值最大的Region区域(根据回收所获得的空间大小以及回收需要的时间综合考虑)所以这就是G1叫做Garbage-First的原因。
G1的工作流程和CMS很相似,区别在最后的步骤。也有四步:
- 初始标记:需要STW,标记下GC Roots关联的对象,并且修改TAMS(Next Top at Mark Start)的值,使得下一阶段并发运行时,能在正确可用的Region中创建对象。
- 并发标记:和CMS一样,主要是进行GC Roots的向下搜索,找出存活对象进行标记。
- 最终标记:需要STW,和CMS一样,这个阶段是修正并发标记期间因用户程序运行而导致变动的对象。
- 筛选回收:对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间制定回收计划。

G1的第一个重点是为运行需要大堆且GC延迟有限的应用程序的用户提供解决方案,这就意味着堆大小约为6G或更大,并且稳定且可预测的暂停时间低于0.5秒。如果应用程序具备以下特性,可以考虑切换到G1收集器:
- 超过50%的Java堆被实时数据占用
- 对象分配率或提升率差异很大
- 当前应用程序GC停顿时间超过0.5秒,又想缩短停顿时间
