Java核心基础十:JVM垃圾回收机制(GC)

一、垃圾回收(GC)核心算法

1. 标记-清除(Mark-Sweep)
  • 步骤

    1. 标记:从GC Roots(如活动线程、静态变量等)出发,标记所有可达对象。

    2. 清除:遍历堆内存,回收未被标记的对象内存。

  • 优点:实现简单,无需移动对象。

  • 缺点

    • 内存碎片:回收后内存不连续,大对象分配困难。

    • 效率问题:两次遍历(标记+清除)耗时较长。

  • 应用场景:CMS收集器的老年代回收阶段。

2. 复制(Copying)
  • 步骤

    1. 将内存分为两块(From、To),每次只使用其中一块。

    2. GC时,将存活对象从From区复制到To区,并清空From区。

  • 优点:无内存碎片,分配高效。

  • 缺点:内存利用率低(需预留一半空间)。

  • 应用场景:新生代的Minor GC(Eden和Survivor区)。

3. 标记-整理(Mark-Compact)
  • 步骤

    1. 标记:同标记-清除。

    2. 整理:将存活对象向内存一端移动,清理边界外碎片。

  • 优点:无内存碎片,适合老年代。

  • 缺点:对象移动导致额外开销。

  • 应用场景:Serial Old、Parallel Old收集器。

4. 分代收集(Generational Collection)
  • 核心思想:根据对象存活周期将堆分为新生代(Young)和老年代(Old),采用不同算法。

    • 新生代:对象存活率低,使用复制算法(Minor GC)。

    • 老年代:对象存活率高,使用标记-清除或标记-整理(Major GC/Full GC)。


二、现代垃圾收集器

1. Serial收集器
  • 特点:单线程执行GC,全程STW(Stop-The-World)。

  • 适用场景:客户端模式或小内存应用(如嵌入式系统)。

  • 参数-XX:+UseSerialGC

2. Parallel收集器(吞吐量优先)
  • 特点:多线程并行GC,关注高吞吐量(吞吐量 = 用户代码运行时间 / 总时间)。

  • 参数

    • -XX:+UseParallelGC(新生代Parallel,老年代Serial Old)。

    • -XX:+UseParallelOldGC(新生代和老代均并行)。

  • 调优参数

    • -XX:ParallelGCThreads设置并行GC线程数(默认CPU核数)。

    • -XX:MaxGCPauseMillis目标最大GC停顿时间(JVM尽力实现)。

3. CMS(Concurrent Mark Sweep)
  • 特点:并发标记清除,减少STW时间,关注低延迟。

  • 步骤

    1. 初始标记(STW):标记GC Roots直接关联的对象。

    2. 并发标记:遍历对象图。

    3. 重新标记(STW):修正并发标记期间变动的对象。

    4. 并发清除:回收垃圾。

  • 缺点

    • 内存碎片问题。

    • 并发阶段占用CPU资源,可能影响应用吞吐量。

  • 参数-XX:+UseConcMarkSweepGC

4. G1(Garbage-First)
  • 特点

    • 将堆划分为多个Region(默认2048个),每个Region可以是Eden、Survivor或Old。

    • 优先回收垃圾最多的Region(Garbage-First),可预测停顿时间。

  • 步骤

    1. 初始标记(STW)。

    2. 并发标记。

    3. 最终标记(STW)。

    4. 筛选回收(STW):选择高收益Region回收。

  • 参数

    • -XX:+UseG1GC

    • -XX:MaxGCPauseMillis=200(目标停顿时间,默认200ms)。

    • -XX:G1HeapRegionSize=32m(Region大小)。

5. ZGC(Z Garbage Collector)
  • 特点

    • 基于染色指针(Colored Pointers)和读屏障(Load Barrier)。

    • 支持TB级堆内存,停顿时间不超过10ms。

    • 无分代(JDK16开始支持分代)。

  • 参数-XX:+UseZGC

6. Shenandoah
  • 特点:与ZGC类似,但通过Brooks指针实现并发整理。

  • 参数-XX:+UseShenandoahGC


三、GC调优

1. 调优目标
  • 低延迟:减少GC停顿时间(如Web服务、实时系统)。

  • 高吞吐量:最大化应用运行时间(如离线计算)。

  • 内存占用:减少堆内存使用(资源受限环境)。

2. 调优步骤
  1. 监控与分析

    • 启用GC日志:-Xlog:gc*:file=gc.log:time,uptime:filecount=10,filesize=50M

    • 使用工具分析:VisualVM、GCViewer、Arthas。

  2. 识别问题

    • 频繁Full GC:老年代空间不足或内存泄漏。

    • 长STW停顿:堆过大或收集器配置不当。

    • 高内存占用:对象生命周期不合理。

3.参数调整
  • 堆大小

    • -Xms-Xmx设为相同值,避免动态扩容(如-Xms4g -Xmx4g)。

  • 新生代比例

    • -XX:NewRatio=2(老年代:新生代=2:1)。

    • -XX:SurvivorRatio=8(Eden:Survivor =8:1:1)。

  • 选择收集器

    • 低延迟:G1或ZGC。

    • 高吞吐量:Parallel GC。

  • 其他参数

    • -XX:MaxTenuringThreshold=15(对象晋升老年代的年龄)。

    • -XX:+AlwaysPreTouch(启动时预分配内存,减少运行时延迟)。

4. 调优案例
  • 案例1:频繁Full GC

    • 现象:老年代快速填满,触发Full GC。

    • 原因:内存泄漏或Survivor区过小导致对象过早晋升。

    • 解决

      • 检查代码中未关闭的资源(如数据库连接)。

      • 增大新生代:-XX:NewRatio=1(老年代:新生代=1:1)。

      • 调整晋升阈值:-XX:MaxTenuringThreshold=10

  • 案例2:G1长时间停顿

    • 现象:G1的Mixed GC阶段耗时过长。

    • 原因:Region大小不合理或并发标记阶段CPU资源争抢。

    • 解决

      • 增大Region大小:-XX:G1HeapRegionSize=32m

      • 限制并发GC线程数:-XX:ConcGCThreads=4

  • 案例3:ZGC低吞吐量

    • 现象:应用吞吐量下降,CPU占用高。

    • 原因:读屏障开销过大。

    • 解决

      • 减少堆内存大小(如从32G降至16G)。

      • 升级至JDK17+,优化ZGC性能。

.

你可能感兴趣的:(Java开发,java,jvm,开发语言)