Skip to content

Java 垃圾回收简介

C/C++ 需要手动管理内存,忘记释放导致内存泄漏,过早释放导致野指针。Java 通过垃圾回收(Garbage Collection,GC)自动管理内存,解放了程序员的双手。但 GC 不是银弹,理解其原理才能避免内存泄漏和 GC 调优问题。

为什么需要垃圾回收

C++ 需要手动 new/delete,忘记释放导致内存泄漏,过早释放导致野指针。

Java 自动回收:对象超出作用域后不再可达,等待 GC 回收。程序员无需关心内存释放,专注于业务逻辑。

垃圾回收算法

引用计数法

每个对象有一个引用计数器,引用为 0 时回收。优点是简单、回收及时;缺点是无法处理循环引用。

可达性分析算法(JVM 使用)

从 GC Roots 开始,通过引用链判断对象是否可达。GC Roots 包括:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中 JNI 引用的对象。

内存分区

┌─────────────────────────────────────────────────────────────┐
│  方法区(Method Area)      │  存储类信息、常量、静态变量    │
├─────────────────────────────────────────────────────────────┤
│                      堆(Heap)                            │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐          │
│  │ Eden 区  │ │ Survivor │ │ Survivor │ │ Old Gen │          │
│  │  (Eden) │ │  Space0  │ │   (To)  │ │(Tenured)│          │
│  └─────────┘ └─────────┘ └─────────┘ └─────────┘          │
├─────────────────────────────────────────────────────────────┤
│  虚拟机栈(VM Stack)      │  方法调用栈帧                  │
├─────────────────────────────────────────────────────────────┤
│  程序计数器(PC)          │  字节码行号指示器               │
└─────────────────────────────────────────────────────────────┘

GC 主要关注堆内存,分代回收:年轻代(大部分对象朝生夕死,GC 频率高)和老年代(存活时间长的对象,GC 频率低)。

垃圾回收器

回收器算法特点适用场景
Serial串行简单高效单线程、小数据量
Parallel并行吞吐量优先科学计算、大数据
CMS并发停顿时间短Web 应用
G1分区可控停顿时间JDK 9+ 默认
ZGC并发极低停顿(<1ms)大内存、低延迟

当前推荐:JDK 8 用 Parallel GC,JDK 9+ 用 G1 GC,大内存低延迟用 ZGC 或 Shenandoah。

GC 类型

类型说明触发条件
Minor GC(Young GC)清理年轻代Eden 区满
Major GC清理老年代老年代满
Full GC清理整个堆多种条件

常见问题

内存泄漏

对象不再使用但无法被回收。例如静态集合持有对象引用:

java
static List&lt;Object&gt; list = new ArrayList&lt;&gt;();
public void add(Object obj) {
    list.add(obj);  // 对象一直被引用,无法回收
}

解决:避免静态集合持有对象引用、及时清理资源、使用弱引用/软引用。

OutOfMemoryError

java
while (true) {
    new Object();  // 无限创建对象导致 OOM
}

解决:设置合理的堆大小 -Xms512m -Xmx512m、检查内存泄漏、优化对象创建。

GC 参数调优

bash
# 设置堆大小
java -Xms512m -Xmx512m MyApp

# 选择 GC 回收器
java -XX:+UseG1GC MyApp      # 使用 G1 回收器
java -XX:+UseZGC MyApp       # 使用 ZGC
java -XX:+UseShenandoahGC MyApp  # 使用 Shenandoah

# 设置停顿时间目标(G1)
java -XX:MaxGCPauseMillis=200 MyApp

后续可阅读:Java 程序运行机制JVM/JRE/JDK 区别编译型 vs 解释型

基于 VitePress 构建