Appearance
JVM GC
GC(Garbage Collection)负责自动识别不再使用的对象,并回收它们占用的内存。学习 GC 不需要先背所有参数,先理解三个问题:对象怎么判断存活、回收会停顿多久、不同回收器在吞吐和延迟之间如何取舍。
GC 解决什么问题
| 问题 | GC 的作用 |
|---|---|
| 对象不用了谁释放 | 自动发现不可达对象并回收 |
| 内存碎片怎么办 | 复制、整理或分区回收 |
| 应用能不能继续跑 | 在 STW、并行、并发之间取舍 |
| 停顿能不能变短 | 使用并发标记、分区回收、读写屏障等技术 |
GC 不保证没有内存泄漏。只要对象仍然被可达引用链持有,GC 就不能回收。
对象存活判断
HotSpot 主要使用可达性分析。GC 从 GC Roots 出发,沿引用链遍历对象:
text
GC Roots -> 可达对象 -> 继续遍历不可达对象才可能被回收。
常见 GC Roots:
- 当前线程栈帧中的局部变量和参数。
- 静态字段引用的对象。
- JNI 引用。
- 活跃线程对象。
- 被锁持有的对象。
- JVM 内部结构引用的对象。
排查内存泄漏时,关键不是“对象很大”,而是“谁一直引用它”。heap dump 分析的核心就是找引用链。
STW、并行、并发
| 概念 | 含义 | 关注点 |
|---|---|---|
| STW | 暂停应用线程,GC 独占执行 | 停顿时间 |
| 并行 | 多个 GC 线程一起工作,但应用线程通常暂停 | 缩短 GC 工作时间 |
| 并发 | GC 线程和应用线程同时运行 | 降低停顿 |
并发回收器也不是完全不停顿。初始标记、最终标记、引用处理、类卸载等阶段仍可能 STW。
常见回收器定位
| 回收器 | 定位 | 常见场景 |
|---|---|---|
| Serial | 简单、单线程、低资源占用 | 小堆、工具程序、单核环境 |
| Parallel | 吞吐量优先 | 批处理、离线计算 |
| ParNew | CMS 时代的新生代并行收集器 | 维护老系统 |
| CMS | 老年代并发标记清除 | 维护低延迟老系统 |
| G1 | 分区、可控停顿、通用服务端 | 大多数服务端应用 |
| ZGC | 超低延迟、大堆并发回收 | 延迟敏感服务 |
| Shenandoah | 超低延迟并发回收 | 延迟敏感服务 |
| Epsilon | 不回收 | 性能测试、短生命周期进程 |
选型
小应用或命令行工具
优先考虑 Serial。它简单、开销低,复杂回收器的并发能力在小堆上不一定划算。
bash
-XX:+UseSerialGC批处理或吞吐优先
优先考虑 Parallel。目标是总处理时间更短,而不是每次暂停都很短。
bash
-XX:+UseParallelGC通用服务端
优先考虑 G1。它在吞吐和停顿之间比较均衡,也是现代服务端常见选择。
bash
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200MaxGCPauseMillis 是目标,不是硬保证。设置过低可能导致 GC 更频繁、CPU 更高。
极低延迟
考虑 ZGC 或 Shenandoah。它们把大部分工作并发化,适合对长暂停敏感的服务。
bash
-XX:+UseZGC低延迟回收器可能用更多 CPU 或牺牲部分吞吐,迁移前要压测。
GC 日志
JDK 9+ 推荐统一日志:
bash
-Xlog:gc*:file=gc.log:time,uptime,level,tags关注字段:
| 指标 | 含义 |
|---|---|
| Pause 时间 | 应用线程暂停多久 |
| Young / Mixed / Full | 回收类型 |
| before/after | 回收前后内存变化 |
| allocation failure | 是否分配失败触发 |
| humongous | G1 大对象相关问题 |
| concurrent cycle | 并发周期是否来得及完成 |
排查路径
Full GC 频繁
- 看 GC 日志确认 Full GC 触发原因。
- 抓 heap dump,分析老年代大对象和引用链。
- 判断是内存泄漏、对象生命周期过长,还是堆太小。
- 再调整堆大小、晋升策略或回收器。
停顿过长
- 先确认停顿发生在 Young、Mixed 还是 Full GC。
- 看对象存活率和回收后释放空间。
- 看是否有大对象、类卸载、引用处理或系统压力。
- 调整目标停顿、堆大小或换低延迟回收器。
GC 后内存不下降
可能原因:
- 对象仍被静态集合、缓存、线程本地变量持有。
- 类加载器泄漏。
- 直接内存或 native 内存增长,不体现在堆里。
- heap dump 抓取时机不对。
常用命令
bash
jstat -gcutil <pid> 1000
jcmd <pid> GC.heap_info
jcmd <pid> GC.class_histogram
jcmd <pid> GC.heap_dump /tmp/app.hprof生产环境抓 dump 前确认磁盘空间和停顿风险。
