JVM调优实战与参数配置

JVM调优实战与参数配置

JVM 内存布局是排查线上问题的基础。很多开发者遇到 OutOfMemoryError 时不知道从哪里入手。本文结合实际案例,讲清楚各区域的作用和常见异常,帮你建立排查思路。

调优前准备

#

1. 明确目标

目标 关注点
低延迟 GC 停顿时间
高吞吐 单位时间处理量
低内存 内存占用
高并发 线程数、连接数

#

2. 收集基线数据

# 压测工具
wrk -t12 -c400 -d30s http://localhost:8080/api

# 监控指标
- 吞吐量 (TPS/QPS)
- 平均/最大响应时间
- GC 频率和停顿时间
- CPU/内存使用率

内存配置

#

堆内存设置

# 基础配置
-Xms4g -Xmx4g # 初始和最大堆内存相同,避免动态调整开销

# 年轻代
-Xmn1g # 年轻代大小
# 或
-XX:NewRatio=2 # 老年代:年轻代 = 2:1

# Survivor区
-XX:SurvivorRatio=8 # Eden:S0:S1 = 8:1:1

原则

  • -Xms = -Xmx,避免运行时扩容
  • 年轻代占堆的 1/4 到 1/3
  • Survivor 区能容纳一次 Young GC 后的存活对象

#

元空间设置

-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m

注意:MaxMetaspaceSize 默认无限制,建议设置上限防止类加载泄漏导致内存耗尽。

#

直接内存

-XX:MaxDirectMemorySize=512m

适用:Netty、NIO 等使用堆外内存的场景。

#

栈内存

-Xss512k    # 每个线程的栈大小

注意:线程数多时应减小,避免总内存占用过高。

GC 选择与配置

#

场景1:Web 应用(低延迟)

# JDK 8
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200

# JDK 11+
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UseStringDeduplication # 字符串去重

#

场景2:批处理(高吞吐)

-XX:+UseParallelGC
-XX:GCTimeRatio=19 # GC时间占比 5%
-XX:+UseAdaptiveSizePolicy

#

场景3:大内存低延迟

# JDK 17+
-XX:+UseZGC
-XX:MaxGCPauseMillis=10
-Xms64g -Xmx64g

GC 日志配置

#

JDK 8

-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-XX:+PrintGCCause
-Xloggc:/var/log/app/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=10M

#

JDK 9+

-Xlog:gc*:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=10m

OOM 时自动 dump

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/app/heapdump.hprof
-XX:OnOutOfMemoryError="sh /app/alert.sh"

生产环境配置模板

#

通用 Web 应用(JDK 11+)

JAVA_OPTS="
-server
-Xms4g -Xmx4g
-Xmn1536m
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+ParallelRefProcEnabled
-XX:+AlwaysPreTouch
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/app/
-Xlog:gc*:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=10m
-XX:ErrorFile=/var/log/app/hs_err_pid%p.log
"

#

高并发 API 服务(JDK 17+)

JAVA_OPTS="
-server
-Xms8g -Xmx8g
-XX:+UseZGC
-XX:MaxGCPauseMillis=5
-XX:+ZGenerational
-XX:+AlwaysPreTouch
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/app/
"

#

微服务容器化

JAVA_OPTS="
-server
-XX:+UseContainerSupport
-XX:InitialRAMPercentage=70.0
-XX:MaxRAMPercentage=70.0
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+UseStringDeduplication
"

典型调优案例

#

案例1:频繁 Full GC

现象

[Full GC (Allocation Failure)  4096M->3890M(4096M), 3.5s]
频繁发生,每次停顿 3~5 秒

分析

  • 堆内存设置过小
  • 老年代增长过快(年轻代太小或晋升阈值太低)
  • 内存泄漏

解决

# 1. 增大堆内存
-Xms8g -Xmx8g

# 2. 增大年轻代
-Xmn3g

# 3. 提高晋升阈值
-XX:MaxTenuringThreshold=15

# 4. 检查内存泄漏(MAT分析heap dump)

#

案例2:Young GC 频繁

现象

[GC pause (G1 Evacuation Pause) (young) 200M->50M(4096M), 0.05s]
每秒发生 5~10 次

分析

  • 年轻代太小
  • 对象生命周期太短
  • 创建对象速度过快

解决

# 增大年轻代
-Xmn2g

# 或调整 Eden 比例
-XX:SurvivorRatio=6

# 代码优化:使用对象池、避免大对象

#

案例3:GC 停顿过长

现象

[GC pause (G1 Evacuation Pause) (mixed) 6000M->3000M(8000M), 1.2s]

分析

  • 堆太大,G1 回收的 Region 过多
  • 大对象过多
  • 存活对象多,复制时间长

解决

# 1. 降低停顿目标
-XX:MaxGCPauseMillis=100

# 2. 使用 ZGC(JDK 17+)
-XX:+UseZGC

# 3. 优化大对象
-XX:G1HeapRegionSize=16m # 增大Region

调优检查清单

  • 明确调优目标(延迟 vs 吞吐)
  • 压测获取基线数据
  • 分析 GC 日志
  • 检查是否有内存泄漏
  • 验证调优效果
  • 监控生产环境

总结

配置项 建议
堆内存 Xms = Xmx,容器环境用百分比
年轻代 堆的 1/4 ~ 1/3
GC JDK 9+ 用 G1,大堆低延迟用 ZGC
日志 开启详细 GC 日志,便于排查
OOM 自动 dump heap

JVM 调优是一个持续的过程:监控 -> 分析 -> 调整 -> 验证。不要过度调优,合理的配置 + 健康的代码才是性能的根本保障。

核心要点

  1. 堆内存分为年轻代和老年代,年轻代又分为 Eden、Survivor 等区域

  2. 栈内存是线程私有的,每个线程都有自己的栈空间

  3. 方法区(元空间)存储类信息、常量池等

  4. 常见的 OOM 类型:HeapSpace、OutOfMemoryError、StackOverflowError

总结

理解 JVM 内存模型是成为高级 Java 工程师的必备技能。在实际项目中,配置合适的堆内存大小、选择合适的垃圾收集器,都需要对内存布局有清晰的认识。


   转载规则


《JVM调优实战与参数配置》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录