Java 的垃圾回收(Garbage Collection,简称 GC)是 JVM(Java 虚拟机)自动内存管理的最核心机制之一,它让开发者不用手动释放内存(不像 C/C++ 需要 free/delete),极大降低了内存泄漏和野指针的风险。
下面按逻辑顺序给你讲清楚目前(JDK 8 → JDK 21+ / 2025-2026 主流版本)的垃圾回收机制:
1. 核心问题:怎么判断一个对象是“垃圾”?
主流判定方式(也是现在几乎全部 GC 都用的):
可达性分析算法(Reachability Analysis)
- 从一系列叫 GC Roots 的对象开始向下搜索
- 能被搜索到的对象 → 存活
- 搜索不到的对象 → 垃圾(可回收)
常见的 GC Roots(2025 年仍然是这些):
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量池中引用的对象
- 本地方法栈中 JNI(Native 方法)引用的对象
- 已启动且未停止的 Java 线程对象
- ……
注意:引用计数法早就被主流 JVM 抛弃了(因为循环引用问题很难解决)。
2. 分代收集理论(Generational Collection)——目前最核心的设计思想
绝大部分对象“朝生夕死”,少数对象能活得很久。
基于这个弱分代假说,JVM 把堆内存分为:
| 区域 | 存放对象特点 | 存活时间 | 常用回收算法 | GC 类型 | 频率 |
|---|---|---|---|---|---|
| Eden | 新创建的对象 | 很短 | — | Minor GC | 很高 |
| Survivor | 经过 1 次 Minor GC 仍存活 | 较短 | 复制算法 | Minor GC | 较高 |
| Old/Tenured | 经过多次 Minor GC 仍存活、被显式晋升 | 很长 | 标记-清除 / 标记-整理 | Major/Full GC | 较低 |
| Metaspace (JDK8+) | 类元数据、常量池等 | 很长 | — | — | — |
年轻代(Young Generation) = Eden + 两个 Survivor(From / To)
老年代(Old Generation) = 除了年轻代以外的堆空间
3. 主流 GC 算法(组合使用)
| 算法名称 | 是否会产生 STW(Stop The World) | 是否移动对象(整理) | 适用场景 | 内存碎片情况 | 备注 |
|---|---|---|---|---|---|
| 标记-清除 | 是 | 否 | 老年代 | 多 | 最基础 |
| 标记-整理 | 是 | 是 | 老年代 | 无 | 老年代整理用 |
| 复制 | 是 | 是(复制到另一半) | 年轻代(Eden → Survivor) | 无 | 空间浪费一半 |
| 标记-复制 | 是 | 是 | 年轻代 | 无 | 现代年轻代主流 |
4. 2025-2026 年主流垃圾收集器对比(最常用)
| 收集器 | 年轻代算法 | 老年代算法 | 是否并发 | 最大暂停时间目标 | 适用场景 | JDK 默认(21+) | 当前推荐度(2026) |
|---|---|---|---|---|---|---|---|
| Serial | 复制 | 标记-整理 | 否 | — | 客户端、小内存单核 | — | 很低 |
| Parallel | 复制 | 标记-整理 | 否 | — | 吞吐量优先、批处理任务 | — | 中等 |
| CMS | 复制 | 标记-清除(并发) | 是 | — | 响应时间敏感(已被 G1 取代) | — | 很低(已废弃) |
| G1 | 复制 | 标记-清除+整理(并发为主) | 是 | 可设置(默认200ms) | 服务端、大多数场景(默认) | JDK9+ 默认 | ★★★★☆ |
| ZGC | 复制 | 并发标记+并发整理 | 是 | <10ms(目标<1ms) | 超大堆、低延迟(8GB–16TB+) | JDK21+ 渐强 | ★★★★★(推荐) |
| Shenandoah | 复制 | 并发标记+并发整理 | 是 | <10ms | 低延迟、对吞吐量要求不高 | — | ★★★★☆ |
| Epsilon | 无 GC | 无 GC | — | — | 内存确定不会耗尽的极致场景 | — | 特殊用途 |
| Generational ZGC | 复制(分代) | 并发整理(分代) | 是 | <1–2ms | JDK21+ 最新低延迟方案 | — | ★★★★★(最强趋势) |
5. 简单总结目前(2025-2026)最推荐的选择逻辑
- 普通 Web / 中小型服务 → G1(-XX:+UseG1GC)仍然是最稳妥默认选择
- 大内存(>8GB)、对延迟敏感(99 分位延迟 <10ms)→ Generational ZGC(-XX:+UseZGC -XX:+ZGenerational)
- 极致吞吐量、不太在乎延迟 → Parallel(-XX:+UseParallelGC)
- 超低延迟实验性场景 → Shenandoah 或 ZGC(非分代)
一句话总结现代 Java GC 趋势:
从“吞吐量优先”(Parallel → CMS → G1) → 转向 “低延迟优先 + 可接受的吞吐量损失”(ZGC / Shenandoah / Generational ZGC)
有具体场景(比如微服务、游戏服务器、大数据、超低延迟交易系统等)的话,可以告诉我,我再帮你推荐最合适的收集器组合和常用调优参数~