JVM 内存模型与 GC
GC 是 Java 的核心特性,也是性能调优的关键。理解各种 GC 算法和调优参数,是高级 Java 工程师的必备技能。
GC 基础概念
哪些对象需要回收?
java
// 可达性分析算法(Reachability Analysis)
// GC Roots 包括:
// 1. 虚拟机栈中引用的对象(局部变量)
// 2. 方法区中静态属性引用的对象
// 3. 方法区中常量引用的对象
// 4. 本地方法栈中 JNI 引用的对象
// 5. JVM 内部引用(基本类型 Class 对象、常驻异常对象等)
// 6. 同步锁持有的对象
// 从 GC Roots 出发,不可达的对象就是"垃圾"
Object a = new Object(); // a 可达
Object b = new Object(); // b 可达
a = null; // 原来 a 指向的对象不可达,可被回收引用类型
java
import java.lang.ref.*;
// 强引用(Strong Reference)— 不会被 GC 回收
Object strong = new Object();
// 软引用(Soft Reference)— 内存不足时回收,适合缓存
SoftReference<byte[]> soft = new SoftReference<>(new byte[1024 * 1024]);
byte[] data = soft.get(); // 可能为 null
// 弱引用(Weak Reference)— 下次 GC 时回收
WeakReference<Object> weak = new WeakReference<>(new Object());
// WeakHashMap 使用弱引用作为 key
// 虚引用(Phantom Reference)— 无法通过虚引用获取对象,用于跟踪 GC 回收
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<>(new Object(), queue);GC 算法
标记-清除(Mark-Sweep)
优点:简单
缺点:产生内存碎片,清除效率低
标记阶段:从 GC Roots 遍历,标记所有可达对象
清除阶段:回收未标记对象
[已用][已用][空闲][已用][空闲][已用][空闲][空闲]
↓ 清除后
[已用][已用][ ][已用][ ][已用][ ]
碎片化!标记-复制(Mark-Copy)
优点:无碎片,分配效率高
缺点:内存利用率 50%
将内存分为两半,每次只用一半
GC 时将存活对象复制到另一半,清空当前半
年轻代 Eden + Survivor 就是此算法的改进版:
Eden(80%) + S0(10%) + S1(10%)
每次 Minor GC:Eden + S0 → S1(或反向)标记-整理(Mark-Compact)
优点:无碎片,内存利用率高
缺点:整理阶段需要移动对象,开销大
标记后将存活对象向一端移动,清理边界外的内存
老年代常用此算法垃圾收集器
Serial GC(串行)
bash
-XX:+UseSerialGC
# 单线程,STW(Stop The World)
# 适合:客户端应用、小内存场景Parallel GC(并行,Java 8 默认)
bash
-XX:+UseParallelGC
-XX:ParallelGCThreads=4 # GC 线程数
-XX:MaxGCPauseMillis=200 # 最大停顿时间目标
-XX:GCTimeRatio=99 # GC 时间占比目标(1/(1+99)=1%)
# 多线程 GC,吞吐量优先
# 适合:批处理、后台计算CMS GC(并发标记清除,已废弃)
bash
-XX:+UseConcMarkSweepGC
# 并发执行,低停顿
# 缺点:内存碎片、CPU 占用高、并发模式失败
# Java 9 废弃,Java 14 移除G1 GC(Java 9+ 默认)
G1 将堆划分为多个等大的 Region(默认 1-32MB)
每个 Region 可以是 Eden/Survivor/Old/Humongous
优势:
- 可预测的停顿时间(-XX:MaxGCPauseMillis)
- 无内存碎片(整理)
- 适合大堆(6GB+)bash
-XX:+UseG1GC # Java 9+ 默认
-XX:MaxGCPauseMillis=200 # 停顿时间目标(ms)
-XX:G1HeapRegionSize=16m # Region 大小
-XX:G1NewSizePercent=5 # 年轻代最小占比
-XX:G1MaxNewSizePercent=60 # 年轻代最大占比
-XX:G1MixedGCLiveThresholdPercent=85 # 混合 GC 触发阈值ZGC(Java 15+ 生产可用)
bash
-XX:+UseZGC
# 停顿时间 < 1ms(与堆大小无关)
# 并发标记、并发整理
# 适合:超大堆(TB 级)、低延迟场景
# Java 21 支持分代 ZGC(-XX:+ZGenerational)Shenandoah GC
bash
-XX:+UseShenandoahGC
# 类似 ZGC,Red Hat 开发
# 并发整理,极低停顿GC 日志分析
bash
# 开启 GC 日志(Java 9+)
-Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=5,filesize=20m
# Java 8
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log# G1 GC 日志示例
[2.345s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause)
[2.345s][info][gc,heap] GC(1) Eden regions: 24->0(24)
[2.345s][info][gc,heap] GC(1) Survivor regions: 3->3(3)
[2.345s][info][gc,heap] GC(1) Old regions: 10->10
[2.345s][info][gc,heap] GC(1) Humongous regions: 0->0
[2.345s][info][gc,heap] GC(1) Metaspace: 45678K->45678K(1093632K)
[2.345s][info][gc] GC(1) Pause Young (Normal) 312M->52M(512M) 12.345ms
# 回收前->回收后(堆大小) 停顿时间常见 GC 问题
Full GC 频繁
java
// 原因 1:老年代空间不足
// 解决:增大堆内存,优化对象生命周期
// 原因 2:元空间不足
// 解决:-XX:MaxMetaspaceSize=512m
// 原因 3:System.gc() 被调用
// 解决:-XX:+DisableExplicitGC
// 原因 4:大对象直接进入老年代
// 解决:-XX:PretenureSizeThreshold=3145728(3MB)内存泄漏排查
bash
# 1. 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>
# 或 OOM 时自动生成
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/
# 2. 用 MAT(Memory Analyzer Tool)分析
# 查找 Leak Suspects
# 分析 Dominator Tree
# 找到持有大量内存的对象链java
// 常见内存泄漏场景
// 1. 静态集合持有对象引用
static List<Object> cache = new ArrayList<>();
// 解决:使用 WeakReference 或定期清理
// 2. 未关闭的资源
// 解决:try-with-resources
try (Connection conn = dataSource.getConnection()) {
// 自动关闭
}
// 3. 内部类持有外部类引用
// 解决:使用静态内部类
// 4. ThreadLocal 未清理
ThreadLocal<Object> tl = new ThreadLocal<>();
tl.set(largeObject);
// 解决:使用后调用 tl.remove()GC 调优实战
bash
# 典型生产配置(Java 17,8核16G)
java \
-Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:G1HeapRegionSize=16m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/app/logs/ \
-Xlog:gc*:file=/app/logs/gc.log:time,uptime:filecount=5,filesize=20m \
-jar app.jar
# 低延迟场景(Java 21,ZGC)
java \
-Xms8g -Xmx8g \
-XX:+UseZGC \
-XX:+ZGenerational \
-XX:MaxGCPauseMillis=10 \
-jar app.jarGC 选择指南
| 场景 | 推荐 GC |
|---|---|
| 吞吐量优先(批处理) | Parallel GC |
| 通用 Web 服务 | G1 GC(默认) |
| 低延迟(< 10ms) | ZGC / Shenandoah |
| 超大堆(> 32GB) | ZGC |
| Java 21 新项目 | ZGC(分代) |