垃圾回收
2026/1/15大约 4 分钟
垃圾回收
如何判断对象可回收
引用计数法
给对象添加引用计数器,有引用时加 1,引用失效时减 1。
缺点:无法解决循环引用问题。
// 循环引用示例
Object a = new Object();
Object b = new Object();
a.ref = b;
b.ref = a;
a = null;
b = null;
// 引用计数法无法回收 a 和 b可达性分析(JVM 使用)
从 GC Roots 出发,遍历引用链,不可达的对象可回收。
GC Roots 包括
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中 JNI 引用的对象
- 同步锁持有的对象
引用类型
| 类型 | 说明 | 回收时机 |
|---|---|---|
| 强引用 | Object obj = new Object() | 不会被回收 |
| 软引用 | SoftReference | 内存不足时回收 |
| 弱引用 | WeakReference | GC 时回收 |
| 虚引用 | PhantomReference | 随时可能被回收 |
软引用示例
// 适合做缓存
SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024 * 1024]);
byte[] data = softRef.get(); // 可能为 null弱引用示例
WeakReference<Object> weakRef = new WeakReference<>(new Object());
System.gc();
Object obj = weakRef.get(); // 很可能为 null垃圾回收算法
标记-清除(Mark-Sweep)
缺点:产生内存碎片
标记-复制(Copying)
优点:无碎片,分配效率高
缺点:内存利用率低(50%)
标记-整理(Mark-Compact)
优点:无碎片
缺点:需要移动对象,效率较低
分代收集
| 区域 | 特点 | 算法 |
|---|---|---|
| 新生代 | 对象存活率低 | 复制算法 |
| 老年代 | 对象存活率高 | 标记-清除/标记-整理 |
垃圾收集器
收集器分类
Serial 收集器
- 特点:单线程,STW
- 适用:Client 模式,小内存
-XX:+UseSerialGCParNew 收集器
- 特点:Serial 的多线程版本
- 适用:配合 CMS 使用
-XX:+UseParNewGC
-XX:ParallelGCThreads=4Parallel Scavenge 收集器
- 特点:关注吞吐量
- 适用:后台计算任务
-XX:+UseParallelGC
-XX:MaxGCPauseMillis=100 # 最大停顿时间
-XX:GCTimeRatio=99 # 吞吐量(1/(1+99)=1%)CMS 收集器
- 特点:低停顿,并发收集
- 算法:标记-清除
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70 # 触发阈值
-XX:+UseCMSCompactAtFullCollection # Full GC 后压缩缺点:
- CPU 敏感
- 无法处理浮动垃圾
- 产生内存碎片
G1 收集器(JDK 9 默认)
- 特点:可预测停顿时间,Region 化内存
- 适用:大内存、多核 CPU
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200 # 目标停顿时间
-XX:G1HeapRegionSize=4m # Region 大小G1 回收过程:
- 初始标记(STW)
- 并发标记
- 最终标记(STW)
- 筛选回收(STW)
ZGC(JDK 11+)
- 特点:超低延迟(<10ms),支持 TB 级内存
- 算法:染色指针 + 读屏障
-XX:+UseZGC
-XX:ZCollectionInterval=5 # GC 间隔收集器对比
| 收集器 | 算法 | 停顿时间 | 吞吐量 | 适用场景 |
|---|---|---|---|---|
| Serial | 复制/标记整理 | 长 | 低 | 小内存 |
| Parallel | 复制/标记整理 | 中 | 高 | 后台任务 |
| CMS | 标记清除 | 短 | 中 | Web 应用 |
| G1 | 标记整理+复制 | 可控 | 中高 | 大内存 |
| ZGC | 标记整理 | 极短 | 高 | 超大内存 |
Minor GC vs Full GC
| 类型 | 触发条件 | 回收区域 |
|---|---|---|
| Minor GC | Eden 区满 | 新生代 |
| Major GC | 老年代满 | 老年代 |
| Full GC | 老年代满/方法区满/System.gc() | 整个堆+方法区 |
对象晋升老年代
- 年龄达到阈值:默认 15 次 Minor GC 后晋升
- 大对象直接进入老年代:超过
-XX:PretenureSizeThreshold - 动态年龄判断:Survivor 中相同年龄对象超过一半
- 空间分配担保失败
GC 调优
常用参数
# 堆内存
-Xms4g -Xmx4g # 初始和最大堆大小
-Xmn2g # 新生代大小
-XX:SurvivorRatio=8 # Eden:Survivor
# GC 日志
-Xlog:gc*:file=gc.log # JDK 9+
-XX:+PrintGCDetails # JDK 8
# 收集器选择
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200调优目标
- 低延迟:减少 GC 停顿时间
- 高吞吐:减少 GC 总时间占比
- 低内存:减少内存占用
调优步骤
- 分析 GC 日志
- 确定问题(频繁 GC、长时间停顿、OOM)
- 调整参数
- 验证效果