JVM 调优实战 —— 从 OOM 到毫秒级响应
JVM 调优实战 —— 从 OOM 到毫秒级响应
1. JVM 内存结构
┌───────────────────────────────────────────┐
│ JVM 内存 │
│ ┌──────────────────┐ ┌───────────────┐ │
│ │ 堆(Heap) │ │ 方法区/元空间 │ │
│ │ ┌────┐ ┌──────┐ │ │ 类信息/常量池 │ │
│ │ │Eden│ │ Old │ │ └───────────────┘ │
│ │ └────┘ └──────┘ │ ┌───────────────┐ │
│ └──────────────────┘ │ 虚拟机栈 │ │
│ ┌──────────────────┐ └───────────────┘ │
│ │ 程序计数器 │ │
│ └──────────────────┘ │
└───────────────────────────────────────────┘
| 区域 | 线程共享 | OOM 风险 |
|---|---|---|
| 堆 | ✅ | Java heap space |
| 方法区/元空间 | ✅ | Metaspace |
| 虚拟机栈 | ❌ | StackOverflowError |
| 程序计数器 | ❌ | 无 |
2. GC 算法演进
2.1 主流 GC 收集器对比
| GC | 适用场景 | 特点 |
|---|---|---|
| Serial | 单核/客户端 | STW,简单高效 |
| Parallel | 多核/批处理 | 吞吐量优先 |
| CMS | 低延迟 | 并发标记,有碎片 |
| G1 | 大堆/均衡 | Region 化,可预测停顿 |
| ZGC | 超低延迟 | 停顿 < 10ms |
3. 常用调优参数
# 堆内存设置(生产环境建议初始=最大,避免扩容开销)
-Xms4g -Xmx4g
# 新生代与老年代比例(默认1:2)
-XX:NewRatio=2
# 使用 G1 收集器
-XX:+UseG1GC
# 设置期望 GC 停顿时间目标(毫秒)
-XX:MaxGCPauseMillis=200
# 打印 GC 日志(JDK 9+)
-Xlog:gc*:file=./gc.log:time,uptime:filecount=5,filesize=20m
# 出现 OOM 时自动 dump 堆快照
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/logs/heap-dump.hprof
4. OOM 排查实战
// 模拟内存泄漏场景
public class MemoryLeakDemo {
private static final List<byte[]> cache = new ArrayList<>();
// 静态集合持有大对象引用,GC 无法回收 → OOM
public static void addCache() {
cache.add(new byte[1024 * 1024]); // 每次加 1MB
}
}
排查步骤:
jmap -dump:format=b,file=heap.hprof <pid>导出堆文件- 用 MAT(Eclipse Memory Analyzer) 打开分析
- 查看 Dominator Tree 找出占用最大的对象
- 定位代码位置,修复泄漏
5. 生产调优案例
问题:服务每隔 4 小时出现 Full GC,停顿 3s,影响用户体验。
分析:
- 老年代占用持续增长
- 存在大量
byte[]对象晋升到老年代 - 发现是 HTTP 响应未及时释放导致的内存泄漏
解决:
# 切换到 ZGC(JDK 17+),停顿降至 < 5ms
-XX:+UseZGC -XX:ZCollectionInterval=300
💡 调优核心原则:让 GC 尽早在新生代回收,减少老年代压力。


💬 文章评论 (0)