作者:诗民宜洁兰丰 | 来源:互联网 | 2024-12-28 13:35
本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版,详细探讨了JVM中不同类型的垃圾收集器及其工作原理。通过介绍各种垃圾收集器的特性和应用场景,帮助读者更好地理解和优化JVM内存管理。
本篇文章主要基于《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》,深入探讨了Java虚拟机中的垃圾收集器(GC),并提供了详细的算法和实现分析。
GC概述
垃圾收集器是Java虚拟机(JVM)用于自动回收不再使用的对象所占用的内存空间的关键组件。不同的JVM实现了多种垃圾收集器,每种收集器都有其特定的算法和适用场景。本文重点讨论HotSpot虚拟机中的垃圾收集器。
上图展示了HotSpot虚拟机中7种垃圾收集器及其组合方式。这些收集器可以分为新生代和老年代两种类型,并且某些收集器之间可以通过双箭头连线表示可以协同工作。
Serial 收集器
Serial 是一个单线程的新生代收集器,使用复制算法。它在进行垃圾收集时会暂停所有其他线程(Stop The World)。尽管这种做法会导致用户程序暂时停止运行,但在某些情况下,如桌面应用程序或单CPU系统中,其简单高效的特点仍然使其成为默认选择。
上图展示了 Serial 和 Serial Old 收集器的工作流程。Serial 收集器采用复制算法处理新生代,而 Serial Old 则使用标记-整理算法处理老年代。
ParNew 收集器
ParNew 是 Serial 的多线程版本,同样使用复制算法处理新生代。除了能够并发执行多个线程外,ParNew 在控制参数、收集算法等方面与 Serial 基本相同。它是许多服务器模式下虚拟机的首选新生代收集器,尤其适合与 CMS 收集器配合使用。
这里展示了 ParNew 和 Serial Old 收集器的运行示意图。ParNew 使用复制算法处理新生代,而 Serial Old 则继续使用标记-整理算法处理老年代。
Parallel Scavenge 收集器
Parallel Scavenge 也是一个多线程的新生代收集器,但它的设计目标是最大化吞吐量而不是最小化停顿时间。因此,它更适合于后台计算任务较多的应用场景。Parallel Scavenge 提供了两个关键参数来调整性能:-XX:MaxGCPauseMillis
控制最大停顿时间和 -XX:GCTimeRatio
设置吞吐量比例。
Serial Old 收集器
Serial Old 是 Serial 收集器的老年代版本,使用标记-整理算法。它主要用于客户端模式下的虚拟机,但在服务端模式下也有两个特殊用途:一是与 Parallel Scavenge 搭配使用;二是在 CMS 发生并发模式失败时作为后备方案。
Parallel Old 收集器
Parallel Old 是 Parallel Scavenge 的老年代版本,使用多线程和标记-整理算法。自 JDK 1.6 起引入,它解决了之前仅能与 Serial Old 配合使用的问题,使得 Parallel Scavenge 和 Parallel Old 成为了一对高效的吞吐量优先组合。
CMS 收集器
CMS (Concurrent Mark Sweep) 是一种以最短回收停顿时间为目标的老年代收集器。它通过四个步骤完成垃圾回收:初始标记、并发标记、重新标记和并发清除。其中,初始标记和重新标记需要暂停所有用户线程,而并发标记和并发清除则可以与用户线程同时进行。
尽管 CMS 收集器具有低停顿的优点,但它也存在一些缺点,例如对 CPU 资源敏感、无法处理浮动垃圾以及可能导致空间碎片等问题。
G1 收集器
G1 (Garbage-First) 是一款面向服务端应用的垃圾收集器,旨在替代 CMS 收集器。G1 不再从物理上区分新生代和老年代,而是将整个 Java 堆划分为多个大小相等的独立区域(Region)。每个 Region 可以根据需要动态地分配给新生代或老年代。
G1 收集器的主要特点包括:
- 并行与并发:充分利用多核处理器的优势,减少 Stop The World 的停顿时间。
- 分代收集:针对新对象和老对象采用不同的处理策略,提高收集效率。
- 空间整合:通过标记-整理和复制算法避免产生空间碎片。
- 可预测的停顿:支持指定最大停顿时间,确保应用性能稳定。
此外,G1 还引入了 Remembered Set 和 Collection Set 等机制,进一步优化了跨代引用的处理过程。
垃圾收集参数
要查询当前使用的垃圾收集器,可以使用以下命令:
java -XX:+PrintCommandLineFlags -version
此命令会显示所有被设置过的 JVM 参数及其值。以下是常用的垃圾收集相关参数及其描述:
参数 |
描述 |
---|
-XX:+UseSerialGC |
启用 Serial 收集器 + Serial Old 组合 |
-XX:+UseParNewGC |
启用 ParNew 收集器 + Serial Old 组合 |
-XX:+UseParallelGC |
启用 Parallel 收集器 + Serial Old 组合 |
-XX:+UseParallelOldGC |
启用 Parallel 收集器 + Parallel Old 组合 |
-XX:+UseConcMarkSweepGC |
启用 CMS 收集器 + ParNew 收集器 + Serial Old 组合 |
-XX:+UseG1GC |
启用 G1 收集器 |
-XX:+PrintGCDetails |
打印详细的 GC 日志信息 |
-XX:+PrintGCTimeStamps |
输出 GC 时间戳(基准时间形式) |
-XX:+PrintGCDateStamps |
输出 GC 时间戳(日期形式) |
-XX:+PrintHeapAtGC |
在 GC 前后打印堆信息 |
-XX:+PrintTenuringDistribution |
打印 Survivor 区的对象年龄分布 |
-Xloggc:$CATALINA_HOME/logs/gc.log |
指定 GC 日志文件路径 |
-XX:NewRatio |
设置新生代与老年代的比例,默认为 2 |
-XX:SurvivorRatio |
设置 Eden 区与 Survivor 区的比例,默认为 8 |
-XX:GCTimeRatio |
设置 GC 时间占总时间的比率,默认为 99% |
-XX:MaxGCPauseMillis |
设置最大 GC 停顿时间 |
-XX:PretenureSizeThreshold |
设置直接晋升到老年代的对象大小 |
-XX:MaxTenuringThreshold |
设置提升到老年代的最大年龄,默认为 15 |
-XX:+UseAdaptiveSizePolicy |
启用自适应调整策略 |
-XX:HandlePromotionFailure |
允许处理晋升失败的情况 |
-XX:ParallelGCThreads |
设置并行收集器的线程数 |
-XX:ParallelCMSThreads |
设置 CMS 收集器的线程数 |
-XX:ConcGCThreads |
设置并发垃圾收集器的线程数 |
-XX:CMSInitiatingOccupancyFraction |
设置触发 CMS 收集的老年代占用率阈值,默认为 68% |
-XX:+UseCMSCompactAtFullCollection |
在 Full GC 后进行内存压缩 |
-XX:CMSFullGCsBeforeCompaction |
设置多少次 CMS 收集后进行一次内存压缩 |
-XX:+CMSClassUnloadingEnabled |
允许卸载类元数据 |
-XX:CMSInitiatingPermOccupancyFraction |
设置触发 CMS 回收的永久区占用率阈值 |
-XX:+UseCMSInitiatingOccupancyOnly |
仅在达到阈值时触发 CMS 回收 |
-XX:InitiatingHeapOccupancyPercent |
设置触发并发标记周期的堆占用率,默认为 45% |
-XX:G1HeapWastePercent |
设置垃圾占比阈值,决定是否发生 Mixed GC |
-XX:G1ReservePercent |
设置保留的假天花板总量,默认为 10% |
-XX:G1HeapRegionSize |
设置每个 Region 的大小,默认根据堆大小自动调整 |