【JVM】3、垃圾收集器如何选选择

127 阅读5分钟

JVM垃圾收集器概述

JVM提供了多种垃圾收集器(GC),每种都有其特点和适用场景。以下是主流垃圾收集器的使用指南:

  1. 小型应用​:Serial GC
  2. 吞吐量优先​:Parallel GC
  3. 延迟敏感​:G1 GC或ZGC/Shenandoah
  4. 超大堆​:G1 GC(JDK8)或ZGC(JDK15+)
  5. 最新技术​:ZGC或Shenandoah

注意:不同JDK版本支持的GC可能不同,建议根据具体JDK版本和应用程序特性选择合适的收集器。

1. Serial收集器

特点​:

  • 单线程收集器
  • 适用于客户端模式
  • 使用标记-复制算法(新生代)和标记-整理算法(老年代)

使用方式​:

markdown
markdown
复制
-XX:+UseSerialGC

适用场景​:

  • 单核CPU环境
  • 小型应用
  • 客户端应用

2. Parallel/Throughput收集器

特点​:

  • 多线程并行收集
  • 关注吞吐量
  • 新生代使用标记-复制,老年代使用标记-整理

使用方式​:

markdown
markdown
复制
-XX:+UseParallelGC
-XX:+UseParallelOldGC

适用场景​:

  • 多核服务器
  • 注重吞吐量的应用
  • 批处理任务

3. CMS(Concurrent Mark-Sweep)收集器

特点​:

  • 并发标记清除
  • 低停顿时间
  • 使用标记-清除算法

使用方式​:

markdown
markdown
复制
-XX:+UseConcMarkSweepGC

适用场景​:

  • 重视响应时间的应用
  • 中等规模堆内存(2-4GB)
  • 老年代收集

4. G1(Garbage-First)收集器

特点​:

  • 分区域收集
  • 可预测停顿时间
  • 标记-整理算法

使用方式​:

markdown
markdown
复制
-XX:+UseG1GC

适用场景​:

  • 大堆内存(6GB以上)
  • 需要平衡吞吐量和延迟
  • JDK9+默认收集器

5. ZGC收集器

特点​:

  • 超低延迟(10ms以下)
  • 可扩展至TB级堆
  • 并发收集

使用方式​:

markdown
markdown
复制
-XX:+UseZGC

适用场景​:

  • 超大堆内存应用
  • 对延迟极度敏感的应用
  • JDK15+生产可用

6. Shenandoah收集器

特点​:

  • 低暂停时间
  • 并发压缩
  • 与ZGC类似但实现不同

使用方式​:

-XX:+UseShenandoahGC

适用场景​:

  • 需要低延迟的应用
  • OpenJDK发行版

常见的垃圾回收算法

JVM(Java Virtual Machine)中的垃圾回收(Garbage Collection, GC)是自动内存管理机制的一部分,主要用于回收不再使用的对象所占用的内存空间。常见的垃圾回收算法主要包括以下几种:

1. 标记-清除算法(Mark-Sweep)

  • 原理
    • 标记阶段:从根节点开始遍历对象图,标记所有可达的对象为存活对象。
    • 清除阶段:遍历整个堆,将未被标记的对象回收。
  • 优点:实现简单。
  • 缺点
    • 会产生内存碎片,可能导致分配大对象时无法找到连续的空间。
    • 清除过程中需要暂停应用线程(Stop-The-World)。

2. 复制算法(Copying)

  • 原理
    • 将内存划分为两个相等的区域(From 和 To)。
    • 每次只使用其中一个区域(From),当该区域满时,将存活的对象复制到另一个区域(To),然后清空原区域。
  • 优点
    • 解决了内存碎片问题。
    • 实现高效,适合年轻代(Young Generation)中大量短命对象的回收。
  • 缺点
    • 内存利用率较低,因为只能使用一半的内存空间。

3. 标记-整理算法(Mark-Compact)

  • 原理
    • 标记阶段:与标记-清除算法相同,标记所有存活对象。
    • 整理阶段:将所有存活对象向一端移动,然后清理掉边界外的内存。
  • 优点
    • 避免了内存碎片问题。
    • 提高了内存利用率。
  • 缺点
    • 整理阶段增加了额外的开销。

4. 分代收集算法(Generational Collection)

  • 原理
    • 将堆内存划分为不同的代(Generation),通常分为年轻代(Young Generation)和老年代(Old Generation)。
    • 年轻代使用复制算法,主要处理短命对象。
    • 老年代使用标记-清除或标记-整理算法,处理长命对象。
  • 优点
    • 根据对象生命周期的不同特点选择合适的算法,提高了GC效率。
    • 减少了全局GC的频率。
  • 常见实现
    • Serial GC:单线程GC,适用于小型应用。
    • Parallel Scavenge GC:多线程GC,适用于吞吐量优先的应用。
    • CMS(Concurrent Mark Sweep) GC:以最短停顿时间为目标的GC,适用于响应时间敏感的应用。
    • G1(Garbage First) GC:将堆划分为多个大小相等的Region,优先回收垃圾最多的Region,适用于大堆内存应用。

5. 增量收集算法(Incremental Collection)

  • 原理
    • 将一次完整的GC过程分成多个小步骤,交替执行GC和应用程序线程,减少单次停顿时间。
  • 优点
    • 降低了长时间停顿对应用的影响。
  • 缺点
    • 实现复杂度较高,且可能引入额外的性能开销。

6. 分区收集算法(Region-based Collection)

  • 原理
    • 将堆内存划分为多个独立的Region(如G1 GC中的Region),每个Region可以独立进行GC。
  • 优点
    • 可以灵活控制GC的粒度,减少全局GC的频率。
    • 支持并行和并发GC操作,提升性能。
  • 典型实现
    • G1 GC 是分区收集算法的典型代表。

总结

算法名称适用场景主要优点主要缺点
标记-清除老年代简单易实现内存碎片化
复制年轻代无内存碎片内存利用率低
标记-整理老年代无内存碎片,内存利用率高整理阶段开销大
分代收集通用场景提高GC效率实现复杂
增量收集对响应时间敏感的应用减少单次停顿时间实现复杂,性能开销大
分区收集大堆内存应用灵活控制GC粒度,减少全局GC实现复杂

在实际应用中,JVM会根据堆的大小、对象生命周期的特点以及应用的需求选择合适的垃圾回收算法。例如,G1 GC结合了分代收集和分区收集的优点,适用于大堆内存和低延迟要求的应用场景。