docs/src/jvm/garbage-collector.md
以上三点构成不可能三角,即一款垃圾回收器不可能同时满足三点。随着硬件水平的提升,内存占用不再是我们关注的重点,评估垃圾回收器性能时,重点关注吞吐量和暂停时间。吞吐量和暂停时间是相互矛盾的,目前我们追求的效果是:在最大吞吐量优先的情况下,减小暂停时间。
7款经典垃圾回收器间的组合关系:
说明:
编写一段简单的java程序:
public class Test {
public static void main(String[] args) {
System.out.println("hello");
}
}
添加-XX:+PrintCommandLineFlagsJVM参数配置,在JDK 8环境下程序输出:
-XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
hello
-XX:+UseParallelGC说明JDK 8默认的垃圾回收器为Parallel。
在JDK 9环境下输出:
-XX:G1ConcRefinementThreads=10 -XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
hello
-XX:+UseG1GC说明JDK 9默认的垃圾回收器为G1。
Serial垃圾回收器为单线程串行回收器,为HotSpot中Client模式下默认的新生代垃圾回收器,采用复制算法、串行回收和STW机制进行内存回收;
Serial Old垃圾回收器为Serial提供的老年代垃圾回收器,采用标记压缩算法、串行回收和STW机制进行内存回收:
Serial适用于运行在Client模式下的虚拟机或者内存不大(几十MB到一两百MB)的环境下,因为是串行的,有较长时间的STW,所以并不适用于要求快响应、交互较强的应用。
可以通过XX:+UseSerialGC参数启用Serial回收器,表示新生代使用Serial,老年代使用Serial Old。
ParNew是Parallel New两个词的简写,是Serial的多线程版本垃圾回收器。ParNew是很多JVM运行在Server模式下新生代的默认垃圾回收器,采用复制算法,并行回收和STW机制进行内存回收。
可以通过XX:+UseParNewGC参数启用ParNew回收器,表示新生代使用ParNew,老年代不受影响。
Serial、ParNew搭配Serial Old回收器示意图:
Parallel Scavenge回收器也是作用于新生代,同样采用复制算法,并行回收和STW机制。
Parallel Scavenge和ParNew对比:
JDK 1.6提供了用于老年代的并行垃圾回收器 —— Parallel Old回收器,用于替代Serial Old回收器。Parallel采用标记压缩、并行回收和STW机制。
可以通过-XX:+UseParallelGC指定新生代使用Parallel Scavenge回收器;-XX:+UseParallelOldGC指定老年代使用Parallel Old回收器,它们是成对存在的,开启一个另一个也会开启。
此外还可以通过-XX:ParallelGCThreads=设置并行回收器的线程数:
-XX:ParallelGCThreads=的值等于CPU数量;-XX:ParallelGCThreads=的值等于3+5*CPU_COUNT/8。-XX:+UseAdaptiveSizePolicy开启Parallel Scavenge的自适应调节策略:
JDK 1.5 HotSpot推出了一款真正意义上的并发回收器 —— CMS(Concurrent-Mark-Sweep),第一次实现了让垃圾回收线程和用户线程同时工作。CMS的关注点在于尽可能缩短垃圾收集时用户线程停顿的时间。
CMS作为一款老年代的垃圾回收器,不能和新生代垃圾回收器Parallel Scavenge搭配使用,只能和ParNew或者Serial搭配使用。
CMS回收器示意图:
主要分为以下几个步骤:
CMS的优缺点都很明显:
优点:
缺点:
此外,CMS在回收过程中,因为用户线程并没有中断,所以还需确保用户线程有足够的内存可用。换句话说,CMS回收器不能等老年代即将被填满时才去回收,而应当堆内存使用率到达一定阈值时,便开始进行回收。如果CMS运行期间预留内存不足,就会出现一次“Concurrent Mode Failure”失败,虚拟机会启动后备方案,临时启用Serial Old回收器来完成老年代的垃圾回收。
CMS回收器可设置参数:
-XX:+UseConcMarkSweepGC,开启CMS GC,开启后,-XX:+UseParNewGC会自动打开;-XX:CMSInitiatingOccupanyFraction=,设置堆内存使用率阈值,一旦达到这个阈值,CMS开始进行回收(JDK5及之前,默认值为68,JDK6及以上版本默认值为92%);-XX:+UseCMSCompactAtFullCollection,指定在CMS回收完老年代后,对内存空间进行压缩处理,以避免碎片化问题;-XX:CMSFullGCsBeforeCompaction,设置执行多少次CMS GC后,对内存空间进行压缩整理;-XX:ParallelCMSThreads=,设置CMS的线程数。默认启动的线程数为(ParallelGCThreads+3)/4。我们知道,当CPU个数小于8时,ParallelGCThreads的默认值为CPU个数,所以对于一个8核CPU,默认启动的CMS线程数为3,换句话说只有62.5%的CPU资源用于处理用户线程。所以CMS不适合吞吐量要求高的场景。G1(Garbage First)回收器把堆内存分割成很多不相关的区域(region,物理上不连续),使用不同区域来表示伊甸园区,幸存者区和老年代。
G1会避免对整个Java堆进行垃圾收集,它会跟踪各个region里垃圾回收的价值大小(回收所获得的空间大小及所需时间的经验值),在后台维护一个优先列表,每次根据允许收集时间,优先回收价值最大的region。
region的说明
G1回收垃圾过程如下图所示:
主要分为以下几个步骤:
G1回收器的优缺点:
优点:
缺点:
G1回收器相关参数设置:
-XX:+UseG1GC,开启G1 GC;-XX:G1HeapRegionSize=,设置region的大小。值为2的幂,范围是1MB到32MB之间,目标是根据最小堆内存大小划分出约2048个区域。所以如果这个值设置为2MB,那么堆最小内存大约为4GB;-XX:MaxGCPauseMillis=,设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到),默认值为200ms;-XX:ParallelGCThread=,设置STW时GC线程数值,最多设置为8;-XX:ConcGCThreads=,设置并发标记的线程数,推荐值为ParallelGCThread的1/4左右;-XX:InitiatingHeapOccupancyPercent=,设置触发并发GC周期的Java堆占用率阈值,超过这个值就触发GC,默认值为45。上面这几款经典的垃圾回收器各有特点,具体使用的时候需要根据具体的情况选用不同的垃圾回收器:
| 垃圾回收器 | 分类 | 作用位置 | 使用算法 | 特点 | 适用场景 |
|---|---|---|---|---|---|
| Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 适用于单CPU环境下的Client模式 |
| ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境Server模式下与CMS配合使用 |
| Parallel | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 |
| Serial Old | 串行 | 老年代 | 标记-压缩算法 | 响应速度优先 | 单CPU环境下的Client模式 |
| Parallel Old | 并行 | 老年代 | 标记-压缩算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 |
| CMS | 并发 | 老年代 | 标记-压缩算法 | 响应速度优先 | 适用于互联网或B/S业务 |
| G1 | 并行与并发 | 新生代、老年代 | 复制算法 标记-压缩算法 | 响应速度优先 | 面向服务端应用 |
Epsilon回收器、Shenandoah回收器、ZGC回收器
参考链接:https://juejin.cn/post/7029155686575521828,整理:沉默王二
GitHub 上标星 10000+ 的开源知识库《二哥的 Java 进阶之路》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:太赞了,GitHub 上标星 10000+ 的 Java 教程
微信搜 沉默王二 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 222 即可免费领取。