Java生产环境JVM调优是分阶段渐进过程:上线前设固定堆、合理新生代与元空间防护并开启GC日志;初期观察老年代趋势、YGC频率及回收器匹配性;稳定期针对性调参并闭环验证;应急期优先止血并复盘根因。

上线前:稳住基础,防抖动
目标是避免启动即卡顿、扩容抖动或早期 OOM,不追求极致性能,只确保底线稳定。
- 堆内存必须固定大小:-Xms 和 -Xmx 设为相同值(如 -Xms4g -Xmx4g),防止运行中堆扩容触发 STW
- 新生代合理预留:-Xmn 设为堆的 1/3~1/2(如堆 4g,则 -Xmn1536m),避免 YGC 过频或对象过早晋升
- 元空间加防护:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m,防动态类加载失控引发 Full GC
- GC 日志全开:-Xloggc:/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps,为后续分析留底
项目运行初期(1~3天):看真实行为,找异常点
服务已承接真实流量,重点观察 GC 行为是否符合预期,识别“隐性问题”。
- 盯紧老年代趋势:用 jstat -gc PID 1000 或 Prometheus+Grafana 看老年代使用率是否缓升——持续上涨大概率存在内存泄漏或大对象误入
- 查 YGC 频率与耗时:YGC 每秒超 3~5 次且 Eden 区总在满状态,说明 -Xmn 偏小;单次 YGC > 50ms,需检查是否有大对象直接分配到老年代
- 确认回收器是否匹配场景:JDK8 低延迟服务用 -XX:+UseConcMarkSweepGC;JDK11+ 微服务默认走 G1,不建议切回 Parallel
线上稳定期(持续迭代):针对性调优,闭环验证
基于监控数据定位具体瓶颈,每次只调 1~2 个参数,上线后至少观察 24 小时,确认效果再推进下一轮。
- 频繁 Full GC:先 dump 堆(jmap -dump:format=b,file=heap.hprof PID),用 MAT 查 dominant heap objects;若发现大量缓存未清理或日志对象堆积,优先改代码;若确认是晋升过快,可调 -XX:MaxTenuringThreshold=15 并增大 SurvivorRatio
- YGC 停顿偏长:检查是否用了 CMS(JDK8)但并发失败导致退化为 Serial Old;升级 JDK11+ 后改用 G1,并加 -XX:MaxGCPauseMillis=200 控制目标停顿
- 吞吐量不足:若 GC 时间占比超 10%,可适度增大堆(如从 4g→6g),但必须同步监控老年代增长速率,避免掩盖泄漏
OOM 或严重卡顿应急期:快速止血,再复盘
不是调参,而是保服务——先恢复,再归因。
立即学习“Java免费学习笔记(深入)”;
- 立即生效动作:开启 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/jvm/dump/,确保下次 OOM 自动抓现场
- 临时缓解措施:若确认是元空间爆满,可临时提高 -XX:MaxMetaspaceSize=1g;若老年代突增,可降级非核心功能减少对象创建
- 事后必做:用 jstack 抓线程栈 + jmap 抓堆快照 + GC 日志三者交叉比对,锁定是代码问题(如静态 Map 不清理)、配置失当(如 -Xmn 过小),还是资源争抢(如线程池打满阻塞 GC 线程)


















