Skip to content

JVM 组成与垃圾回收详解

一、JVM 中有什么?

1. 类加载子系统
负责加载 .class 文件到内存,包含加载、链接(验证、准备、解析)、初始化三个阶段。

2. 运行时数据区(核心)

  • :对象实例存储区域(GC 主战场)
  • 方法区:JDK8 后改为元空间(Metaspace),使用本地内存存储类元数据
  • 虚拟机栈:存储方法调用栈帧(局部变量表、操作数栈等)
  • 程序计数器:当前线程执行的字节码行号指示器
  • 本地方法栈:Native 方法调用

3. 执行引擎
解释器(逐行解释字节码)、JIT 编译器(热点代码编译为机器码)、GC(垃圾回收器)

4. 本地接口(JNI)
调用操作系统本地库(如 C/C++ 代码)


二、垃圾回收(GC)

1. 是什么?
自动回收堆内存中不再使用的对象,释放资源防止内存泄漏。

2. 解决什么问题?

  • 手动内存管理易导致内存泄漏/溢出
  • 提升开发效率,避免 delete/free 操作
  • 保证程序稳定性(如避免 OutOfMemoryError

3. 对象存活判定

  • 引用计数法(Java 未采用):循环引用问题
  • 可达性分析(Java 采用):从 GC Roots(栈、静态变量等)出发扫描引用链

三、常用 GC 算法

1. 标记-清除(Mark-Sweep)

  • 过程:标记存活对象 → 清除未标记对象
  • 问题:内存碎片
  • 场景:老年代 CMS 回收器

2. 标记-复制(Mark-Copy)

  • 过程:内存分为两块,存活对象复制到另一块 → 清空当前块
  • 优点:无碎片
  • 缺点:空间利用率 50%
  • 场景:新生代(Eden + Survivor)

3. 标记-整理(Mark-Compact)

  • 过程:标记存活对象 → 向一端移动 → 清理边界外内存
  • 优点:无碎片
  • 缺点:移动对象成本高
  • 场景:老年代 Serial Old、G1

四、JDK8+ 主流 GC 回收器

回收器区域算法特点JDK版本
Parallel新生代/老年代复制+标记整理吞吐量优先8默认
CMS老年代标记清除低延迟,碎片问题8
G1全堆分区+标记整理平衡吞吐/延迟,JDK9+默认9+
ZGC全堆着色指针+读屏障<10ms 停顿,TB级堆11+
Shenandoah全堆转发指针+读屏障低延迟,与ZGC竞争12+

五、GC 参数调优

1. 核心参数

bash
# 堆内存设置
-Xms4g -Xmx4g        # 初始堆=最大堆(避免动态扩容)
-XX:NewRatio=2       # 老年代:新生代=2:1
-XX:SurvivorRatio=8  # Eden:Survivor=8:1:1

# GC 回收器选择
-XX:+UseG1GC         # 启用 G1(JDK9+默认)
-XX:+UseZGC          # 启用 ZGC(JDK15+生产可用)

# 目标停顿时间(G1/ZGC)
-XX:MaxGCPauseMillis=200  # 期望最大停顿 200ms

2. 调优步骤

  1. 监控:使用 jstat -gc <pid> 或 VisualVM 分析 GC 日志
  2. 定位问题
    • 频繁 Full GC:老年代空间不足 → 增大堆或调整 NewRatio
    • Young GC 时间长:Eden 过大 → 减小 -Xmn
  3. 优化策略
    • 高吞吐场景:Parallel GC + 增大堆
    • 低延迟场景:G1/ZGC + 限制 MaxGCPauseMillis

3. GC 日志分析

bash
-XX:+PrintGCDetails -Xloggc:gc.log

示例日志:

java
[GC (Allocation Failure) [PSYoungGen: 1024K->512K(1536K)] 
 2048K->1024K(5632K), 0.002s]
  • Allocation Failure:Eden 区空间不足触发 Young GC
  • PSYoungGen:Parallel Scavenge 回收新生代
  • 1024K->512K:回收前占用 → 回收后占用(含 Survivor)

六、重要注意事项

  1. 元空间溢出:JDK8 后 Metaspace 使用本地内存,需监控 -XX:MaxMetaspaceSize
  2. G1 调优
    • 避免设置过小 -XX:G1HeapRegionSize(默认根据堆计算)
    • 混合回收阈值:-XX:G1MixedGCLiveThresholdPercent=85(存活对象低于 85% 才回收)
  3. ZGC 限制
    • JDK15 前为实验特性
    • 需要 Linux x64/macOS 环境
  4. 生产建议
    • 优先使用 G1(JDK8+)
    • 超大规模堆(>32GB)用 ZGC/Shenandoah

总结

组件核心要点
JVM 结构堆(对象存储)、元空间(类元数据)、栈(方法调用)
GC 目的自动管理堆内存,防止内存泄漏
GC 算法复制(新生代)、标记清除/整理(老年代)
JDK8+ GCParallel(吞吐优先)、G1(平衡)、ZGC(超低延迟)
调优核心根据场景选回收器,监控 GC 日志,调整堆/代大小和停顿目标

最佳实践

  1. 生产环境 JDK11+ 默认用 G1
  2. 先用 -Xms=-Xmx 固定堆大小
  3. 通过 MaxGCPauseMillis 控制延迟
  4. 定期分析 GC 日志(工具:GCeasy、Grafana)