JVM调优实战与参数配置
JVM 内存布局是排查线上问题的基础。很多开发者遇到 OutOfMemoryError 时不知道从哪里入手。本文结合实际案例,讲清楚各区域的作用和常见异常,帮你建立排查思路。
调优前准备
#
1. 明确目标
| 目标 | 关注点 |
|---|---|
| 低延迟 | GC 停顿时间 |
| 高吞吐 | 单位时间处理量 |
| 低内存 | 内存占用 |
| 高并发 | 线程数、连接数 |
#
2. 收集基线数据
# 压测工具 |
内存配置
#
堆内存设置
# 基础配置 |
原则:
-Xms=-Xmx,避免运行时扩容- 年轻代占堆的 1/4 到 1/3
- Survivor 区能容纳一次 Young GC 后的存活对象
#
元空间设置
-XX:MetaspaceSize=128m |
注意:MaxMetaspaceSize 默认无限制,建议设置上限防止类加载泄漏导致内存耗尽。
#
直接内存
-XX:MaxDirectMemorySize=512m |
适用:Netty、NIO 等使用堆外内存的场景。
#
栈内存
-Xss512k # 每个线程的栈大小 |
注意:线程数多时应减小,避免总内存占用过高。
GC 选择与配置
#
场景1:Web 应用(低延迟)
# JDK 8 |
#
场景2:批处理(高吞吐)
-XX:+UseParallelGC |
#
场景3:大内存低延迟
# JDK 17+ |
GC 日志配置
#
JDK 8
-XX:+PrintGCDetails |
#
JDK 9+
-Xlog:gc*:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=10m |
OOM 时自动 dump
-XX:+HeapDumpOnOutOfMemoryError |
生产环境配置模板
#
通用 Web 应用(JDK 11+)
JAVA_OPTS=" |
#
高并发 API 服务(JDK 17+)
JAVA_OPTS=" |
#
微服务容器化
JAVA_OPTS=" |
典型调优案例
#
案例1:频繁 Full GC
现象:
[Full GC (Allocation Failure) 4096M->3890M(4096M), 3.5s] |
分析:
- 堆内存设置过小
- 老年代增长过快(年轻代太小或晋升阈值太低)
- 内存泄漏
解决:
# 1. 增大堆内存 |
#
案例2:Young GC 频繁
现象:
[GC pause (G1 Evacuation Pause) (young) 200M->50M(4096M), 0.05s] |
分析:
- 年轻代太小
- 对象生命周期太短
- 创建对象速度过快
解决:
# 增大年轻代 |
#
案例3:GC 停顿过长
现象:
[GC pause (G1 Evacuation Pause) (mixed) 6000M->3000M(8000M), 1.2s] |
分析:
- 堆太大,G1 回收的 Region 过多
- 大对象过多
- 存活对象多,复制时间长
解决:
# 1. 降低停顿目标 |
调优检查清单
- 明确调优目标(延迟 vs 吞吐)
- 压测获取基线数据
- 分析 GC 日志
- 检查是否有内存泄漏
- 验证调优效果
- 监控生产环境
总结
| 配置项 | 建议 |
|---|---|
| 堆内存 | Xms = Xmx,容器环境用百分比 |
| 年轻代 | 堆的 1/4 ~ 1/3 |
| GC | JDK 9+ 用 G1,大堆低延迟用 ZGC |
| 日志 | 开启详细 GC 日志,便于排查 |
| OOM | 自动 dump heap |
JVM 调优是一个持续的过程:监控 -> 分析 -> 调整 -> 验证。不要过度调优,合理的配置 + 健康的代码才是性能的根本保障。
核心要点
堆内存分为年轻代和老年代,年轻代又分为 Eden、Survivor 等区域
栈内存是线程私有的,每个线程都有自己的栈空间
方法区(元空间)存储类信息、常量池等
常见的 OOM 类型:HeapSpace、OutOfMemoryError、StackOverflowError
总结
理解 JVM 内存模型是成为高级 Java 工程师的必备技能。在实际项目中,配置合适的堆内存大小、选择合适的垃圾收集器,都需要对内存布局有清晰的认识。