Skip to content
清晨的一缕阳光
返回

Java 垃圾回收机制详解

Java 垃圾回收机制详解

垃圾回收是 JVM 自动内存管理的核心机制,理解 GC 原理对性能调优至关重要。

一、为什么需要 GC

1.1 内存管理对比

语言内存管理优点缺点
C/C++手动 malloc/free灵活、可控易泄漏、悬空指针
Java自动 GC安全、省心STW、性能开销

1.2 垃圾回收目标

  1. 准确性:不回收存活对象,不遗漏垃圾对象
  2. 高效性:减少 STW(Stop-The-World)时间
  3. 低延迟:避免长时间停顿

二、如何判断对象可回收

2.1 引用计数法(已淘汰)

// 对象被引用一次,计数 +1
// 引用失效,计数 -1
// 计数为 0 时可回收

// ❌ 无法解决循环引用
A objA = new A();
B objB = new A();
objA.setB(objB);
objB.setA(objA);
// 即使不再使用,计数也不为 0

2.2 可达性分析(GC Roots)

GC Roots
├── 虚拟机栈引用的对象
├── 静态属性引用的对象
├── 常量引用的对象
└── 本地方法栈引用的对象(JNI)

算法流程

从 GC Roots 向下搜索

可达的对象 → 存活
不可达的对象 → 可回收

三、垃圾回收算法

3.1 标记 - 清除(Mark-Sweep)

flowchart LR
    A[标记阶段] --> B[清除阶段]
    B --> C[产生碎片]
    

特点

3.2 标记 - 复制(Copying)

新生代内存
├── Eden 区(80%)
├── Survivor0 区(10%)
└── Survivor1 区(10%)

GC 时:
Eden + Survivor0 → Survivor1(存活对象)
然后清空 Eden 和 Survivor0

特点

3.3 标记 - 整理(Mark-Compact)

标记存活对象

向一端移动

清理边界外内存

特点

3.4 分代收集(主流)

堆内存分代
├── 新生代(Young)
│   ├── 对象朝生夕死
│   └── 复制算法
└── 老年代(Old)
    ├── 对象存活率高
    └── 标记 - 整理算法

四、垃圾收集器

4.1 收集器对比

graph LR
    subgraph 新生代
        A[Serial]
        B[ParNew]
        C[Parallel Scavenge]
    end
    
    subgraph 老年代
        D[Serial Old]
        E[CMS]
        F[Parallel Old]
        G[G1]
    end
    
    A --> D
    B --> E
    C --> F

4.2 Serial 收集器

单线程收集
├── 简单高效(单 CPU 首选)
└── STW 时间长

适用场景:客户端应用、内存较小

4.3 ParNew 收集器

Serial 的多线程版本
├── 多线程并行
└── 需要多 CPU 支持

适用场景:配合 CMS 使用

4.4 Parallel Scavenge

关注吞吐量
├── 自适应调节策略
└── -XX:MaxGCPauseMillis 设置最大停顿

适用场景:后台运算、科学计算

4.5 CMS(Concurrent Mark Sweep)

并发收集
├── 初始标记(STW)
├── 并发标记
├── 重新标记(STW)
└── 并发清除

特点

适用场景:B/S 架构、重视响应速度

4.6 G1(Garbage First)

Region 分区
├── Eden / Survivor / Old / Humongous
└── 可预测停顿时间模型

特点

适用场景:服务端、大内存、低延迟


五、GC 日志分析

5.1 开启 GC 日志

# JDK 8
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/var/log/gc.log

# JDK 9+
-Xlog:gc*:file=gc.log:time,uptime,level,tags

5.2 日志解读

[GC (Allocation Failure) 
 [PSYoungGen: 1024K->256K(2048K)]  // 新生代回收
 1536K->768K(4096K),               // 堆整体回收
 0.0012345 secs]                   // 耗时

5.3 常用工具

工具作用
jstat实时查看 GC 统计
GCViewer可视化分析 GC 日志
GCEasy在线分析工具
MAT内存泄漏分析

六、调优参数

6.1 基础参数

# 堆大小
-Xms2g -Xmx2g              # 固定堆大小(避免动态调整)
-Xmn512m                   # 新生代大小

# 收集器选择
-XX:+UseG1GC               # 使用 G1
-XX:+UseConcMarkSweepGC    # 使用 CMS

# 最大停顿时间
-XX:MaxGCPauseMillis=200   # 目标最大停顿(G1)

6.2 高级参数

# 晋升年龄
-XX:MaxTenuringThreshold=15  # 最大晋升年龄

# 大对象直接进入老年代
-XX:PretenureSizeThreshold=1m

# 元空间
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m

七、调优实践

7.1 调优目标

高吞吐量:最大化用户线程执行时间
低延迟:最小化 GC 停顿时间
平衡:根据业务需求权衡

7.2 调优步骤

1. 监控 → 2. 分析 → 3. 调整 → 4. 验证 → 5. 迭代

7.3 常见问题

问题原因解决
频繁 Minor GC新生代过小增大 -Xmn
Full GC 频繁老年代不足/内存泄漏增大 -Xmx,排查泄漏
GC 时间过长堆过大/对象过多优化代码,调整 Region
OOM内存不足增大堆,排查泄漏

八、总结

GC 核心要点:

算法特点适用
标记 - 清除简单、有碎片不单独使用
标记 - 复制高效、无碎片新生代
标记 - 整理无碎片、成本高老年代
收集器特点场景
G1可预测停顿大堆、低延迟
CMS并发收集低停顿、小堆
Parallel高吞吐后台计算

GC 调优是系统工程,需要结合业务场景、监控数据持续优化。


分享这篇文章到:

上一篇文章
Java 类加载机制详解
下一篇文章
JVM 内存模型详解