常用垃圾回收器介绍
1. Serial 收集器
原理与特点 : Serial 收集器是最基本、历史最悠久的垃圾收集器之一。它是一个单线程 的收集器,在进行垃圾回收时,必须暂停所有其他用户线程(“Stop-The-World” - STW),直到垃圾回收完成。这种机制简单高效,没有线程切换的开销,因此在单 CPU 环境或小内存(几十 MB 到一两百 MB)的客户端应用中表现良好.
算法 : 在新生代,Serial 收集器使用标记-复制算法 ;在老年代,其对应的 Serial Old 收集器使用标记-整理算法 .
适用场景 : 主要用于单核处理器或客户端应用,对实时性要求不高的场景.
2. ParNew 收集器
原理与特点 : ParNew 收集器可以看作是 Serial 收集器的多线程版本 . 除了使用多条线程进行垃圾收集外,其行为(包括收集算法、Stop-The-World、对象分配规则等)与 Serial 收集器基本一致. 它同样需要暂停所有用户线程进行垃圾回收. 在多核处理器环境下,ParNew 通常比 Serial 效率更高,但在单核环境下,由于线程切换开销,性能可能反而更差.
算法 : 在新生代使用标记-复制算法 .
适用场景 : 主要用于新生代的垃圾收集,并且是许多运行在 Server 模式下的虚拟机中首选的新生代收集器,因为它是除了 Serial 收集器外,目前唯一能与CMS 收集器 配合工作的收集器.
3. Parallel Scavenge 收集器
原理与特点 : Parallel Scavenge 收集器也是一个用于新生代的多线程 、并行 收集器,同样采用复制算法 . 它与 ParNew 最大的不同在于其设计目标是吞吐量优先 . 这里的吞吐量指的是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值,即“运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)”.
自适应调节策略 : 该收集器提供自适应调节策略 (-XX:+UseAdaptiveSizePolicy),JVM 可以根据当前系统的运行情况,动态调整新生代大小、Eden 与 Survivor 区的比例、晋升老年代对象年龄等参数,以达到最佳的吞吐量或停顿时间目标.
JVM 参数 :
-XX:MaxGCPauseMillis=N: 设置 GC 最大停顿时间的目标,收集器会尝试保证内存回收花费的时间不超过设定值。过小的设置可能导致新生代空间变小,GC 频率增加,反而降低吞吐量.
-XX:GCTimeRatio=N: 设置垃圾收集时间占总时间的比率,即吞吐量的倒数.
适用场景 : 适用于后台计算,不需要太多交互任务,强调高吞吐量的应用.
4. CMS (Concurrent Mark Sweep) 收集器
原理与特点 : CMS 收集器是一款以获取最短回收停顿时间 为目标的收集器,它第一次实现了让垃圾收集线程与用户线程基本上同时工作,从而降低了 GC 停顿时间. 它采用标记-清除算法 .
工作流程 : 主要分为四个阶段:
初始标记 (Initial Mark) : 标记 GC Roots 直接关联到的对象,需要STW ,但耗时短.
并发标记 (Concurrent Mark) : 从初始标记的对象开始,遍历整个对象图,标记所有可达对象。此阶段与用户线程并发执行 ,耗时较长但无需 STW.
重新标记 (Remark) : 修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,需要STW ,但耗时通常比初始标记阶段长,不过仍远低于并发标记.
并发清理 (Concurrent Sweep) : 清理已标记为不可达的对象。此阶段与用户线程并发执行 ,不需 STW.
缺点 :
内存碎片 : 采用“标记-清除”算法,容易产生大量不连续的内存碎片. 当碎片过多导致无法为大对象分配连续内存时,可能触发一次 Full GC,导致长时间的 STW,并可能退化为 Serial Old 收集器作为后备方案.
浮动垃圾 : 并发清理阶段用户线程仍在运行,可能产生新的垃圾(浮动垃圾),这些垃圾本次无法回收,只能留到下一次 GC.
对 CPU 资源敏感 : 并发阶段虽然不暂停用户线程,但会占用部分 CPU 资源,可能导致应用程序整体性能下降.
JVM 参数 :
-XX:CMSInitiatingOccupancyFraction=N: 设置 CMS 收集器触发 GC 的老年代使用阈值百分比。过低会增加 GC 频率,过高可能导致并发模式失败(Concurrent Mode Failure),进而触发 Full GC.
-XX:+UseCMSCompactAtFullCollection: 在 Full GC 后进行内存碎片整理。虽然可以解决内存碎片问题,但会延长 Full GC 的停顿时间.
5. G1 (Garbage-First) 收集器
原理与特点 : G1 收集器是 JDK 7 引入,并在 JDK 9 成为默认的垃圾收集器. 它的设计目标是取代 CMS,在低延迟和高吞吐量之间寻找平衡. G1 引入了分区(Region)概念 ,将整个 Java 堆划分为多个大小相等的独立 Region 块. 每个 Region 可以扮演新生代的 Eden、Survivor 或者老年代空间.
可预测停顿时间 : G1 最大的特点是可预测的停顿时间模型 . 用户可以通过参数设定期望的 GC 停顿时间,G1 会通过记录每个 Region 的回收价值(包括回收后的空间大小、回收所需时间等),维护一个优先级列表,优先回收垃圾最多、回收效率最高的 Region.
算法 : 从整体上看,G1 基于标记-整理算法 实现,可以避免内存碎片;从局部(两个 Region 之间)上看,则是基于标记-复制算法 实现.
巨型对象 (Humongous Objects) : 对于超过一个 Region 一半大小的对象,G1 会将其视为巨型对象,直接分配到特殊的 Humongous Region 中,这些 Region 被视为老年代的一部分.
垃圾回收周期 :
Young GC : 主要收集年轻代 Region,采用标记-复制算法.
Mixed GC : 当老年代占用空间超过阈值(InitiatingHeapOccupancyPercent,默认 45%)时,G1 会启动一次混合回收周期. Mixed GC 会收集整个年轻代以及部分老年代 Region.
并发标记 : G1 的并发标记过程与 CMS 类似,包括初始标记(STW)、根区域扫描、并发标记、重新标记(STW)和清理(STW,但此阶段不清理垃圾对象)等阶段. G1 使用原始快照(SATB)来解决并发标记时对象引用变化的问题.
JVM 参数 :
-XX:+UseG1GC: 启用 G1 收集器.
-XX:MaxGCPauseMillis=N: 设置 GC 最大停顿时间的目标,G1 会尝试达到此目标.
优点 : 解决了 CMS 的内存碎片问题,并提供了可预测的停顿时间.
缺点 : 相比 CMS,G1 的内存占用和运行时额外负载更高,因为每个 Region 都需要维护一个记忆集(Remember Set),可能占用堆容量的 20%甚至更多内存.
6. ZGC (Z Garbage Collector)
原理与特点 : ZGC 是 JDK 11 引入的低延迟垃圾收集器,目标是实现极低的停顿时间 (通常不超过 10 毫秒,甚至可以达到 1 毫秒以内),且停顿时间不随堆大小(支持 8MB 到 4TB,未来可达 16TB)或活跃对象数量的增加而增加. 它几乎不暂停用户线程 .
核心技术 :
染色指针 (Colored Pointers) : ZGC 的关键技术之一。它将对象的状态信息(如是否被标记、是否被重定位)存储在对象指针的高位,而不是对象头中. 这样,GC 线程可以直接通过指针颜色判断对象状态,无需额外内存访问. ZGC 为每个对象创建了三个虚拟内存地址,通过指针指向不同的虚拟内存地址来表示不同的染色标记.
读屏障 (Load Barriers) : ZGC 的另一个关键技术。每次应用线程加载一个对象引用时,都会触发读屏障. 读屏障会检查指针的颜色,如果对象已被移动,读屏障会确保返回对象的新地址. 通过这种方式,ZGC 能够在并发移动对象时保持内存访问的一致性,几乎消除了 STW 暂停.
算法 : ZGC 采用标记-复制算法 ,但在标记、转移和重定位阶段几乎都是并发执行的.
工作流程 : ZGC 只有三个需要 STW 的阶段:初始标记、再标记和初始转移. 这几个阶段的处理时间都与 GC Roots 的数量成正比,通常耗时非常短. 大部分工作(如并发标记、并发转移和重定位)都在用户线程运行的同时进行.
适用场景 : 对延迟要求极高,并且可能拥有超大堆内存(数十 GB 乃至 TB 级别)的应用场景.