Java 中的垃圾回收器随着 JDK 版本的演进不断发展,不同版本支持的垃圾回收器也有所不同。
Serial GC
- JDK 版本: 所有 JDK 版本
- 作用位置: 新生代
- 算法: 标记-复制算法
- 原理: 单线程进行垃圾回收,暂停所有其他线程。 标记存活对象,将存活对象复制到另一个空间,清理原空间。
- 使用场景: 客户端应用或内存较小的应用,简单高效。
- 使用注意事项: 停顿时间长,不适合对响应时间要求高的应用。
- 参数调优:
- -XX:+UseSerialGC: 开启 Serial GC
- -Xms: 设置堆内存初始大小
- -Xmx: 设置堆内存最大大小
ParNew GC
- JDK 版本: 所有 JDK 版本 (JDK 9 之后被标记为废弃)
- 作用位置: 新生代
- 算法: 标记-复制算法
- 原理: Serial GC 的多线程版本,并行进行垃圾回收。
- 使用场景: 与 CMS GC 搭配使用,作为 CMS GC 的新生代垃圾收集器。
- 使用注意事项: 停顿时间较 Serial GC 短,但仍会暂停所有其他线程。
- 参数调优:
- -XX:+UseParNewGC: 开启 ParNew GC
- -XX:ParallelGCThreads: 设置并行垃圾回收线程数
Parallel GC
- JDK 版本: 所有 JDK 版本
- 作用位置: 新生代
- 算法: 标记-复制算法
- 原理: 与 ParNew GC 类似,但更加关注吞吐量。
- 使用场景: 注重吞吐量的多核服务器应用。
- 使用注意事项: 会暂停所有其他线程,停顿时间较长。
- 参数调优:
- -XX:+UseParallelGC: 开启 Parallel GC
- -XX:ParallelGCThreads: 设置并行垃圾回收线程数
- -XX:MaxGCPauseMillis: 设置最大垃圾回收停顿时间
SerialOld GC
- JDK 版本: 所有 JDK 版本
- 作用位置: 老年代
- 算法: 标记-整理算法
- 原理: Serial GC 的老年代版本,单线程进行垃圾回收,标记存活对象,将存活对象整理到空间的一端,清理剩余空间。
- 使用场景: 与 Serial GC 搭配使用,或作为 CMS GC 发生 Concurrent Mode Failure 时的备用方案。
- 使用注意事项: 停顿时间长,不适合对响应时间要求高的应用。
- 参数调优: 无特殊参数
ParallelOld GC
- JDK 版本: JDK 5 update 6 引入
- 作用位置: 老年代
- 算法: 标记-整理算法
- 原理: Parallel GC 的老年代版本,多线程进行垃圾回收。
- 使用场景: 注重吞吐量且需要有效利用多核 CPU 资源的应用程序。
- 使用注意事项: 会暂停所有其他线程,停顿时间较长。
- 参数调优:
- -XX:+UseParallelOldGC: 开启 Parallel Old GC
CMS GC(ConcurrentMarkSweep)
- JDK 版本: JDK 1.4 引入,JDK 9 标记为废弃,JDK 14 移除
- 作用位置: 老年代
- 算法: 标记-清除算法
- 原理: 并发标记清除垃圾回收器,可以与应用程序并发执行,降低垃圾回收停顿时间。包括初始标记、并发标记、重新标记、并发清除等阶段。CMS 收集器在大部分时间与应用程序并发执行,只有在初始标记和重新标记阶段会有短暂的停顿。在老年代进行并发标记和并发清除,减少了垃圾回收对应用程序的影响。
- 使用场景: 以获取最短回收停顿时间为目标的收集器。它在老年代进行垃圾回收,追求低停顿,适用于对响应时间要求较高的应用。
- 使用注意事项:
- 无法处理浮动垃圾:在并发清理阶段,应用程序仍然在运行,会产生新的垃圾,这些垃圾被称为“浮动垃圾”,CMS 无法在本次 GC 中处理它们,只能留到下次 GC 处理
- CPU 资源敏感:CMS 收集器会占用一部分 CPU 资源,可能导致应用程序性能下降
- 产生内存碎片:由于 CMS 收集器是基于标记-清除算法,会导致内存碎片化,可能引发 Full GC
- 参数调优:
- -XX:+UseConcMarkSweepGC: 开启 CMS GC
- -XX:CMSInitiatingOccupancyFraction: 设置老年代空间使用率达到多少时开始 CMS GC(堆占用率阈值)
- -XX:+UseCMSCompactAtFullCollection: 在 Full GC 后进行内存碎片整理
G1 GC(Garbage-First)
- JDK 版本: JDK 6 update 14 引入,JDK 9 成为默认垃圾回收器
- 作用位置: 新生代和老年代
- 算法: 标记-复制算法 (新生代) + 标记-整理算法 (老年代)
- 原理: 将堆内存划分为多个 Region,优先回收垃圾最多的 Region。使用标记-复制算法清理新生代,使用标记-整理算法清理老年代。
- 使用场景: 较大堆内存的应用,对停顿时间有一定要求。大内存应用: G1 适合堆内存较大的应用,例如几 GB 或几十 GB。停顿时间敏感应用: 对于需要低延迟和可预测停顿时间的应用,G1 是理想选择。
- 使用注意事项: 需要较多的 CPU 资源,G1 需要更多的 CPU 和内存资源来维护 Region 信息和执行并发操作。
- 优点:可预测的停顿时间: G1 致力于满足用户设定的停顿时间目标,通过增量收集和并发标记阶段实现更可控的停顿时间。高效的空间利用: G1 将堆划分为多个区域 (Region),并优先收集垃圾最多的区域,有效利用空间并减少碎片化。并行与并发: G1 利用多线程并行执行垃圾收集,同时部分阶段与应用程序并发执行,降低停顿时间。适应性强: G1 能适应不同的堆大小和应用程序,无需过多手动调优。
- 参数调优:
- -XX:+UseG1GC: 开启 G1 GC
- -XX:MaxGCPauseMillis: 设置最大垃圾回收停顿时间(默认值 200ms),需要注意的是,设置过低的停顿时间目标可能会导致吞吐量下降。
- -XX:G1HeapRegionSize: 设置 Region 大小(1M - 32M) JVM 会尝试将堆内存分割成约 2048 个 Region,Region 大小并非固定不变,JVM 会根据运行情况动态调整。通常情况下(默认1M),无需手动设置 Region 大小,Region 的默认大小取决于堆内存的总大小。但如果应用程序分配大量大对象,可以考虑增加 Region 大小,以避免频繁的 Humongous 分配。虑使用 G1 的 Humongous 分配策略,例如 -XX:G1HeapWastePercent 和 -XX:G1MixedGCCountTarget
- -XX:ConcGCThreads :设置并发标记线程数,通常设置为并行垃圾回收线程数的 1/4,可根据 CPU 核心数适当调整,提高并发处理能力。
- -XX:InitiatingHeapOccupancyPercent :设置触发并发标记的堆内存占用百分比,默认值为 45%
ZGC(ZGarbageCollector)
- JDK 版本: JDK 11 引入
- 作用位置: 新生代和老年代
- 算法: 基于指针着色的并发整理算法
- 原理: 可扩展的低延迟垃圾收集器,使用着色指针和读屏障技术,并发进行标记、转移和重定位等操作。
- 使用场景: 超大堆内存的应用,对停顿时间要求极高。如果你正在开发需要管理大量数据,并且对响应时间要求极高的应用程序,ZGC 是一个值得考虑的选择。
- 优势:可扩展性: ZGC 能够有效地处理超大堆内存,官方宣称可以支持 TB 级别的堆内存,而且停顿时间不会随着堆的大小增加而显著增长。这使得它非常适合需要管理大量数据的应用程序,例如大数据处理、机器学习和内存数据库等。
低延迟: ZGC 的首要目标是低延迟,它通过并发执行大部分垃圾回收操作,将停顿时间控制在毫秒级别,即使是 TB 级别的堆内存也能保持稳定的低延迟。 这对于对响应时间要求极高的应用程序至关重要,例如金融交易系统、实时数据处理和高频交易等。
并发处理: ZGC 的大部分操作都是并发执行的,这意味着垃圾回收过程与应用程序线程同时进行,不会造成长时间的停顿。 这提高了应用程序的吞吐量和响应速度,并提供更流畅的用户体验。
有效避免内存碎片: ZGC 采用基于 Region 的内存管理方式,并支持并发整理,能够有效地避免内存碎片问题。 这确保了大对象的分配不会受到阻碍,并提高了内存利用率。 - 缺点:吞吐量: ZGC 为了实现低延迟,牺牲了一部分吞吐量,在相同硬件条件下,吞吐量可能略低于 G1 垃圾回收器。
CPU 资源消耗: ZGC 的并发处理需要消耗一定的 CPU 资源,在大规模应用中需要考虑 CPU 资源的充足性。
成熟度: 相比于 CMS 和 G1 等垃圾回收器,ZGC 仍然是一个相对较新的技术,成熟度和稳定性可能略逊一筹 - 使用注意事项: 需要较新的 JDK 版本
- 参数调优:
- -XX:+UseZGC: 开启 ZGC
Shenandoah GC
- JDK 版本: JDK 12 引入
- 作用位置: 新生代和老年代
- 算法: 基于Brooks指针转发技术的并发整理算法
- 原理: 使用转发指针和读屏障技术,并发进行标记、转移和重定位等操作。
- 使用场景: 对停顿时间要求极高,并且希望减少垃圾收集对应用程序吞吐量影响的场景。
- 使用注意事项: 需要较新的 JDK 版本
- 参数调优:
- -XX:+UseShenandoahGC: 开启 Shenandoah GC
垃圾回收算法对比
算法名称 | 原理 | 优缺点 |
标记-清除 | 标记阶段:从根节点开始遍历所有可达对象,标记为存活对象。 | 优点:效率不高,标记和清除过程都需要遍历堆内存。 |
标记-整理 | 标记阶段:与标记-清除算法相同。 | 优点:没有内存碎片。 缺点:效率低于复制算法 |
标记-复制 | 将堆内存分为两块相同大小的区域,每次只使用其中一块。垃圾回收时,将存活对象复制到另一块区域,然后清空当前区域。 | 优点:效率高,没有内存碎片。适用于存活对象较少的场景(新生代) 缺点:内存利用率低,只有一半内存可用。存活对象较多时,复制成本高 |
分代收集 | 根据对象存活周期的不同,将堆内存分为新生代和老年代。 | 优点: 针对不同特点的对象采用不同的算法,提高效率。 |
资料参考
《深入理解Java虚拟机:JVM高级特性与最佳实践》 - 作者:周志明
《2024年最值得收藏的JVM学习路线》
https://javabetter.cn/xuexiluxian/java/jvm.html
本文暂时没有评论,来添加一个吧(●'◡'●)