Java 垃圾收集器详解:CMS, G1, ZGC
Java 虚拟机(JVM)内置的垃圾收集机制是 Java 程序能够自动管理内存的关键。随着 Java 应用程序规模的增长和技术的进步,垃圾收集器的设计也在不断演进,以满足更高性能、更低延迟的需求。本文将详细介绍 CMS、G1 和 ZGC 这三种垃圾收集器,并提供一个详细的对比表格。
CMS(Concurrent Mark Sweep)
CMS 收集器是一种以获得最短回收停顿时间为目标的收集器,非常适合对响应时间有严格要求的应用场景。CMS 使用多线程并行执行垃圾收集工作,从而减少应用程序暂停的时间。
特点
- 并发清理:CMS 在进行垃圾收集时,会尽量与其他应用程序线程一起工作,减少停顿时间。
- 低停顿时间:通过并行执行,可以显著减少 GC 的停顿时间。
- 内存碎片问题:CMS 不进行内存压缩,可能导致内存碎片。
命令行参数
启动 JVM 并指定使用 CMS 收集器可以通过以下参数:
java -XX:+UseConcMarkSweepGC -Xms200m -Xmx200m -jar yourapp.jar
代码示例
public class CMSExample {
public static void main(String[] args) {
byte[] placeholder;
while (true) {
placeholder = new byte[1024 * 1024]; // 分配1MB的空间
placeholder = null; // 允许这个对象被收集
}
}
}
G1(Garbage First)
G1 是一种兼顾吞吐量和停顿时间的垃圾收集器,它将堆划分为多个大小相等的区域(region),并能独立地回收这些区域。这种设计使得 G1 能够更好地预测和控制 GC 停顿时间。
特点
- 分区式回收:G1 将堆划分为多个小块,每一块都可以独立地被回收。
- 可预测性:通过设置期望的最大停顿时间,G1 可以更好地适应不同应用的需求。
- 吞吐量优化:通过选择合适的区域进行回收,可以在保证性能的同时减少停顿时间。
命令行参数
启动 JVM 并指定使用 G1 收集器可以通过以下参数:
java -XX:+UseG1GC -Xms200m -Xmx200m -jar yourapp.jar
ZGC(Z Garbage Collector)
ZGC 是为了解决大堆(数十 GB 到数 TB)下的垃圾收集问题而设计的。ZGC 的目标是在任何情况下都能保持较低的 GC 停顿时间,即使是在大堆的情况下也是如此。
特点
- 低延迟:ZGC 设计的目标是确保 GC 停顿时间不超过 10 毫秒,无论堆的大小是多少。
- 并发标记:ZGC 使用并发标记算法来减少应用程序的停顿时间。
- 无碎片压缩:ZGC 在每次回收后都会进行内存压缩,以减少内存碎片。
命令行参数
启动 JVM 并指定使用 ZGC 收集器可以通过以下参数:
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms200m -Xmx200m -jar yourapp.jar
对比表格
为了更直观地对比这几种垃圾收集器,以下是一个包含主要特性和指标的对比表格:
垃圾收集器 | 发布版本 | 特点 | 主要优点 | 主要缺点 | 最佳使用场景 |
---|---|---|---|---|---|
Serial | JDK 1.0 | 单线程 | 简单,消耗资源少 | 吞吐量低,不适合多核处理器 | 单核机器上的小型应用 |
Parallel | JDK 1.3 | 多线程,吞吐量优先 | 高吞吐量 | 较长的停顿时间 | 需要最大化吞吐量的应用 |
ParNew | JDK 1.4 | 并发,多线程年轻代收集器 | 并发收集,适合多核处理器 | 仅年轻代,不支持老年代 | 多核机器上的应用 |
CMS | JDK 5 | 并发标记,低停顿时间 | 低停顿时间 | 内存碎片,不适合大堆 | 对响应时间敏感的应用 |
G1 | JDK 7u40 | 并发标记,分区式收集 | 可预测停顿时间,适合大堆 | 较高的 CPU 使用率 | 对停顿时间和吞吐量都有要求的应用 |
ZGC | JDK 11 | 低延迟,无碎片压缩 | 极低的停顿时间,适合大堆 | 较高的内存占用 | 对停顿时间有严格要求的大堆应用 |
Shenandoah | JDK 9 | 低延迟,无碎片压缩 | 极低的停顿时间,适合大堆 | 实验性质较强 | 对停顿时间有严格要求的大堆应用 |
Epsilon | JDK 11 | 无操作收集器 | 几乎没有 GC 开销 | 不实际用于生产环境 | 测试 GC 开销 |
命令行参数示例
以下是使用不同垃圾收集器的一些命令行参数示例:
-
Serial
java -Xms200m -Xmx200m -jar yourapp.jar
-
Parallel
java -XX:+UseParallelGC -Xms200m -Xmx200m -jar yourapp.jar
-
ParNew
java -XX:+UseParNewGC -Xms200m -Xmx200m -jar yourapp.jar
-
CMS
java -XX:+UseConcMarkSweepGC -Xms200m -Xmx200m -jar yourapp.jar
-
G1
java -XX:+UseG1GC -Xms200m -Xmx200m -jar yourapp.jar
-
ZGC
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms200m -Xmx200m -jar yourapp.jar
-
Shenandoah
java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms200m -Xmx200m -jar yourapp.jar
-
Epsilon
java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms200m -Xmx200m -jar yourapp.jar
总结
选择合适的垃圾收集器需要综合考虑应用程序的特点和性能需求。如果需要最小化应用程序的停顿时间,可以选择 CMS 或者 ZGC;如果需要平衡吞吐量和停顿时间,则 G1 是一个不错的选择。对于大堆的应用,ZGC 和 Shenandoah 提供了极低的停顿时间,而 Parallel 和 ParNew 更适合需要高吞吐量的应用。未来随着 Java 技术的发展,新的垃圾收集器将会不断出现,以满足更多样化的需求。