Skip to content

JVM 垃圾回收机制

如何判断对象是否存活?

1. 引用计数法

每个对象有一个引用计数器,被引用时 +1,引用失效时 -1,计数为 0 则可回收。

缺点:无法解决循环引用问题。

java
// 循环引用示例
Object a = new Object();
Object b = new Object();
a.ref = b;  // a 引用 b
b.ref = a;  // b 引用 a
a = null;
b = null;
// 此时 a、b 对象互相引用,计数都不为 0,但实际上已不可达

2. 可达性分析(JVM 使用)

GC Roots 出发,沿引用链遍历,不可达的对象即为垃圾。

GC Roots 包括:
├── 虚拟机栈中引用的对象
├── 方法区中类静态属性引用的对象
├── 方法区中常量引用的对象
├── 本地方法栈中 JNI 引用的对象
└── 被同步锁(synchronized)持有的对象

垃圾回收算法

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

标记阶段: [■][□][■][□][■][□][■]
清除阶段: [■][  ][■][  ][■][  ][■]

缺点:产生内存碎片

2. 标记-复制(Copying)

将内存分为两块,每次只使用一块,GC 时将存活对象复制到另一块。

Eden + S0                          S1
[■][□][■][□][ ][ ][ ]     →     [■][■][ ][ ][ ][ ][ ]

优点:无碎片,适合年轻代(存活对象少)

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

标记后,将存活对象向一端移动,然后清理边界外的内存。

标记:   [■][□][■][□][■][□][■]
整理:   [■][■][■][■][  ][  ][  ]

优点:无碎片,适合老年代(存活对象多)

分代收集策略

区域特点使用算法
年轻代对象存活率低标记-复制
老年代对象存活率高标记-清除 / 标记-整理

垃圾收集器

年轻代收集器

Serial

单线程收集器,GC 时会 Stop-The-World。

bash
-XX:+UseSerialGC

ParNew

Serial 的多线程版本,常与 CMS 配合使用。

bash
-XX:+UseParNewGC

Parallel Scavenge

注重吞吐量,适合后台计算型任务。

bash
-XX:+UseParallelGC
-XX:MaxGCPauseMillis=100    # 最大 GC 停顿时间
-XX:GCTimeRatio=99          # 吞吐量目标

老年代收集器

Serial Old

Serial 的老年代版本,单线程,标记-整理算法。

Parallel Old

Parallel Scavenge 的老年代版本,多线程,标记-整理算法。

CMS(Concurrent Mark Sweep)

以最短停顿时间为目标,采用标记-清除算法。

初始标记 (STW) → 并发标记 → 重新标记 (STW) → 并发清除
bash
-XX:+UseConcMarkSweepGC

缺点

  • 产生内存碎片
  • 占用 CPU 资源
  • 无法处理浮动垃圾

G1 收集器(JDK 9 默认)

将堆划分为多个 Region,可预测停顿时间。

┌────┬────┬────┬────┬────┬────┐
│ E  │ S  │ O  │ O  │ E  │ H  │
├────┼────┼────┼────┼────┼────┤
│ O  │ E  │ S  │ O  │ O  │ E  │
└────┴────┴────┴────┴────┴────┘
E: Eden  S: Survivor  O: Old  H: Humongous
bash
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200    # 期望最大停顿时间
-XX:G1HeapRegionSize=4m     # Region 大小

ZGC(JDK 11+)

低延迟收集器,停顿时间 < 10ms,支持 TB 级堆内存。

bash
-XX:+UseZGC

收集器对比

收集器算法线程适用场景
Serial复制/整理单线程客户端模式
ParNew复制多线程配合 CMS
Parallel复制/整理多线程高吞吐量
CMS标记-清除并发低延迟
G1标记-整理并发大内存低延迟
ZGC标记-整理并发超大内存超低延迟

GC 日志分析

bash
# 开启 GC 日志
-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=10M
[0.015s][info][gc] Using G1
[1.234s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 
                    25M->5M(256M) 12.345ms

关键指标

  • GC 频率
  • 停顿时间
  • 堆内存变化