JVM 调优实战 —— 从 OOM 到毫秒级响应

玄武2026-03-05字数:514阅读:约 2 分钟jvmJVMGC性能调优

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
    }
}

排查步骤

  1. jmap -dump:format=b,file=heap.hprof <pid> 导出堆文件
  2. MAT(Eclipse Memory Analyzer) 打开分析
  3. 查看 Dominator Tree 找出占用最大的对象
  4. 定位代码位置,修复泄漏

5. 生产调优案例

问题:服务每隔 4 小时出现 Full GC,停顿 3s,影响用户体验。

分析

  • 老年代占用持续增长
  • 存在大量 byte[] 对象晋升到老年代
  • 发现是 HTTP 响应未及时释放导致的内存泄漏

解决

# 切换到 ZGC(JDK 17+),停顿降至 < 5ms
-XX:+UseZGC -XX:ZCollectionInterval=300

💡 调优核心原则:让 GC 尽早在新生代回收,减少老年代压力

💬 文章评论 (0)

支持 Markdown,请友善交流
正在加载评论...