Skip to content

JVM 垃圾回收机制详解 ​

垃圾回收(GC)是 Java 相比 C/C++ 最大的优势之一。理解 GC 的工作原理,是排查内存问题和性能调优的基础。

分代模型 ​

JVM 堆内存分为年轻代(Young Generation)和老年代(Old Generation)。

mermaid
graph LR
    Heap[JVM Heap] --> Young[年轻代]
    Heap --> Old[老年代]
    Young --> Eden[Eden 区]
    Young --> S1[Survivor 0]
    Young --> S2[Survivor 1]

对象的生命周期 ​

  1. 新对象分配在 Eden 区
  2. Eden 满了触发 Minor GC,存活对象复制到 Survivor 0
  3. 来回交换,年龄达到阈值(默认 15)的对象晋升到老年代
  4. 老年代满了触发 Major GC / Full GC

为什么用复制算法?年轻代中绝大多数对象「朝生夕死」,存活率极低,复制少量存活对象比标记清除高效得多。

垃圾判定:可达性分析 ​

从 GC Roots 出发,沿引用链遍历,不可达的对象就是垃圾。

GC Roots 包括:虚拟机栈中的局部变量、方法区中的静态变量和常量、本地方法栈中的 JNI 引用、活跃线程。

mermaid
graph TD
    Root1[GC Root: 局部变量] --> A[对象 A]
    Root2[GC Root: 静态变量] --> B[对象 B]
    A --> C[对象 C]
    B --> C
    D[对象 D] --> E[对象 E]
    E --> D

上图中 D 和 E 互相引用但没有 GC Root 到达它们,会被回收。

经典垃圾回收器 ​

Serial GC ​

单线程回收,适合小型应用和客户端场景。-XX:+UseSerialGC

Parallel GC(吞吐量优先) ​

Java 8 默认。多线程回收,目标是最大化吞吐量。

bash
-XX:+UseParallelGC
-XX:MaxGCPauseMillis=200

G1 GC(停顿时间优先) ​

Java 9+ 默认。把堆划分为多个 Region,每次只回收收益最高的 Region。

bash
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200

G1 的回收过程:初始标记(STW)→ 并发标记 → 最终标记(STW)→ 筛选回收(STW)。

G1 的核心优势:可预测的停顿时间。用户指定目标停顿时间,G1 估算能在该时间内回收多少 Region。

ZGC(低延迟) ​

Java 11 引入,停顿时间 < 1ms(Java 16+),适合超大堆和低延迟场景。

如何选择 GC ​

GC适用场景停顿时间
Serial单核、小堆(< 100MB)长
Parallel批处理、离线计算中
G1通用、中等堆(4-32GB)短可预测
ZGC大堆、低延迟极短(< 1ms)

经验法则:堆 < 4GB 用 G1,堆 > 32GB 用 ZGC,只关心吞吐量用 Parallel。

排查内存问题 ​

查看 GC 日志 ​

bash
# Java 9+
java -Xlog:gc*=info:file=gc.log:time,level,tags:filecount=5,filesize=10m -jar app.jar

使用 jstat ​

bash
jstat -gcutil <pid> 1000 10  # 每秒一次,共 10 次

使用 jmap ​

bash
jmap -histo <pid> | head -20        # 堆直方图
jmap -dump:format=b,file=heap.hprof <pid>  # 导出堆 dump

用 MAT 或 JProfiler 分析 heap.hprof,查找内存泄漏。

常见内存问题 ​

内存泄漏:静态集合持有对象引用不放。

java
// 典型泄漏:静态 Map 只 put 不 remove
public class Cache {
    private static final Map<String, Object> CACHE = new HashMap<>();
}

调优建议 ​

bash
-Xms4g -Xmx4g                          # 堆大小固定
-XX:+UseG1GC                            # 使用 G1
-XX:MaxGCPauseMillis=200                # 目标停顿 200ms
-XX:+HeapDumpOnOutOfMemoryError        # OOM 时自动 dump
-XX:HeapDumpPath=/tmp/heap.hprof

小结 ​

JVM GC 的核心思路是分代收集:年轻代用复制算法(存活率低),老年代用标记-清除或标记-整理。理解分代模型、可达性分析和常用 GC 日志排查方法,就能解决大部分内存问题。

基于 VitePress 构建

总访问量 访客数