点击上方“IT那活儿”公众号--专注于企业全栈运维技术分享,不管IT什么活儿,干就完了!!!
GC回收策略
JVM的垃圾回收器大体上的分类主要包括四种:
串行、并行、并发(CMS)和G1。
串行垃圾回收器(Serial)
它为单线程环境设计并且只使用一个线程进行垃圾回收,会暂停所有的用户线程。所以不适合服务器环境。
并行垃圾回收器(Parallel)
多个垃圾回收线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理等弱交互场景。
并发垃圾回收器(CMS)
用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程。互联网公司多用它,适用于对响应时间有要求的场景。
G1垃圾回收器
G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收。
收集器:
新生代回收器
Serial、ParNew、Parallel Scavenge
老年代回收器
CMS、Serial Old、Parallel Old
全堆
G1
收集器 | 类型 | 区域 | 算法 | 目标 | 适用场景 |
Serial | 串行 | 年轻代 | 复制 | 响应速度优先 | 单CPU环境下的client模式 |
Serial Old | 串行 | 老年代 | 标记整理 | 响应速度优先 | 单CPU环境下的client模式、CMS的后备预案 |
ParNew | 并行 | 年轻代 | 复制 | 响应速度优先 | 多CPU环境时在server模式下与CMS配合 |
Parallel Scavenge | 并行 | 年轻代 | 复制 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
Parallel Old | 并行 | 老年代 | 标记-整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
CMS | 并发 | 老年代 | 标记-清除 | 响应速度优先 | 集中在互联网站或B/S系统服务端上的java应用 |
G1 | 并发 | 堆 | 标记-整理+复制 | 响应速度优先 | 面向服务端程序,将来替换CMS |
CMS与G1垃圾回收
2.1 CMS垃圾回收
使用的是标记清除算法,属于老年代垃圾回收的一种,所以在使用过程中配合年轻代垃圾回收,效果更佳。

特点
CMS并不是独占的回收器,也就说CMS回收的过程中,应用程序仍然在不停的工作,又会有新的垃圾不断的产生,所以在使用CMS的过程中应该确保应用程序的内存足够可用。
缺点
如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器;SerialOldGC进行垃圾回收,这会导致应用程序中断,直到垃圾回收完成后才会正常工作。
2.2 G1垃圾回收
使用的是标记整理算法,该回收策略虽然名字中还有年轻代、老年代等名词,但实际上G1已经将Java堆划分为多个大小相等的独立区域(Region),每一个小方格代表一个Region,JVM最多可以有2048个Region。

特点
不需要其他垃圾回收策略配合,能独自完成年轻代、老年代的回收。
缺点
G1使用的是标记整理,但局部(两个Region之间)是复制算法,所以比其他回收策略,会更占内存资源。所以G1对于内存的要求比较高,一般建议在8G或8G+,可使用G1。
G1从整体上来看是 标记-整理 算法,但从局部(两个Region之间)是复制算法。 CMS是 标记-清除算法。 所以说,G1不会产生内存碎片,而CMS会产生内存碎片。 CMS使用了 写后屏障来维护卡表。 G1不仅使用了写后屏障来维护卡表,还用了写前屏障来跟踪并发时的指针变化情况(为了实现原始快照)。 CMS对Java堆内存使用的是传统的 新生代和老年代划分方法。 G1使用的全新的划分方法。 CMS收集器只收集老年代,可以配合新生代的Serial和ParNew收集器一起使用。 G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用。 CMS使用 增量更新解决并发标记下出现的错误标记问题。 G1使用原始快照解决。
疑问解答
针对GC时间,即STW时间的控制,目前只有G1可通过参数-XX:MaxGCPauseMills=60000(毫秒)来控制GC时间,其他垃圾回收策略无法控制,只能使用默认。所以根据每个垃圾回收方式的特性,执行GC的时间也不同。
GC过程中,如果因内存不足,不管是CMS还是G1,都会导致应用程序中断,直到垃圾回收完成后才会正常工作。
当JVM中存在大对象时,使用G1就会更占内存资源,这个情况下,需要扩大内存。使用CMS回收时,会因内存使用率增长过快,而导致内存不足,这种情况下,也需要扩大内存。

如上图,当出现异常时,会频繁触发fullgc,每次fullgc时,都会因内存不足,触发应用中断。
需要更改,如果使用CMS方式,则需要搭配ParNew来配合,并配置其他参数。如:
-XX:+UseConcMarkSweepGC
老年代回收策略
-XX:+UseParNewGC
年轻代回收策略
-XX:CMSInitiatingOccupancyFraction=70
当老年代空间使用率达到70%时,触发CMS回收
-XX:+UseCMSInitiatingOccupancyOnly
指定使用上面设定的阈值,令其生效
-XX:+CMSScavengeBeforeRemark
开启或关闭在CMS重新标记阶段之前的清除(YGC)尝试
-XX:+UseCMSCompactAtFullCollection
该参数和下面的参数一起使用,表示每次FULLGC后,都会对剩余内容进行整理压缩
-XX:CMSFullGCsBeforeCompaction=0
-XX:ParallelGCThreads=8
执行并行GC的线程数
-XX:+DisableExplicitGC
禁止显式执行GC,不允许通过代码来触发GC
-XX:+PrintGCDetails
打印GC日志
-XX:+PrintGCDateStamps
-Xloggc:${com.bes.instanceRoot}/logs/gc/gc.log.%p.%t
因业务请求及内存使用的不稳定性,不建议调整GC的触发时间。
最佳的GC触发方式为java堆达到某个阈值时,触发清理。

本文作者:刘玉柱(上海新炬中北团队)
本文来源:“IT那活儿”公众号





