JVM 调优方案

一、JVM 调优核心思想

在深入具体方案之前,必须明确两个核心思想:

  1. 调优的目的通常是为了解决以下问题:

    • GC 停顿时间过长:应用出现卡顿。
    • 吞吐量下降:单位时间内处理的请求变少。
    • 内存溢出:发生 OutOfMemoryError。
    • CPU 负载过高:频繁的 GC 或线程竞争导致 CPU 资源紧张。
  2. 一切依赖于数据和场景:调优必须基于监控数据(GC 日志、堆快照、性能分析工具),并根据应用类型(Web 服务、大数据计算、交易系统)来制定策略。

二、常用 JVM 调优参数

通常将参数分为堆内存、垃圾收集器、GC 日志、其他性能相关几大类。

2.1 基础堆内存参数
bash 复制代码
# 设置初始堆大小
-Xms512m

# 设置最大堆大小  
-Xmx2048m

# 设置年轻代大小
-Xmn512m

# 设置元空间大小(Java 8+)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m

# 设置永久代大小(Java 7及之前)
-XX:PermSize=128m
-XX:MaxPermSize=256m
  1. -Xms 和 -Xmx

    • 说明:设置堆的初始大小和最大大小。通常将它们设为相同的值,以避免在运行时动态调整堆大小带来的性能波动。
    • 示例:-Xms4g -Xmx4g (设置堆大小为 4GB)
  2. -Xmn

    • 说明:设置年轻代的大小。整个堆 = 年轻代 + 老年代。增大年轻代会减小老年代。这个参数对 GC 性能影响很大。
    • 示例:-Xmn2g (在 4G 堆中,年轻代占 2G,老年代占 2G)。通常设置为整个堆大小的 1/3 到 1/2。
  3. -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize

    • 说明:设置元空间的初始大小和最大大小。在 Java 8 中,永久代被移除,由元空间取代。如果动态生成类较多(如 Spring 的 CGLib),需要限制其大小以防吃光所有内存。
    • 示例:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
  4. -Xss

    • 说明:设置每个线程栈的大小。如果系统有大量线程,过大的栈大小会占用过多内存。过小则可能引起 StackOverflowError。
    • 示例:-Xss256k (对于大多数 Web 应用,256k 足够)
2.2 堆内存比例参数
bash 复制代码
# 新生代与老年代比例(默认约1:2)
-XX:NewRatio=2

# 设置年轻代中 Eden 和 Survivor 的比例
-XX:SurvivorRatio=8        # Eden:S0:S1 = 8:1:1

# 设置晋升老年代的年龄阈值
-XX:MaxTenuringThreshold=15

# 设置大对象直接进入老年代的阈值
-XX:PretenureSizeThreshold=1m

# 设置TLAB(线程本地分配缓冲区)大小
-XX:TLABSize=64k
2.3 垃圾收集器相关

选择并优化合适的垃圾收集器是调优的核心。

  1. 并行收集器(吞吐量优先)

    • 参数:-XX:+UseParallelGC / -XX:+UseParallelOldGC

    • 说明:JDK 8 的默认收集器,关注高吞吐量。适合后台运算、大数据处理等不需要低延迟的场景。

    • 示例:

      bash 复制代码
      # 使用Parallel GC
      -XX:+UseParallelGC
      
      # 设置并行GC线程数
      -XX:ParallelGCThreads=4
      
      # 设置最大GC暂停时间目标
      -XX:MaxGCPauseMillis=100
      
      # 设置吞吐量目标(GC时间与总时间比例)
      -XX:GCTimeRatio=99
  2. CMS 收集器(低延迟)

    • 参数:-XX:+UseConcMarkSweepGC

    • 说明:以获取最短停顿时间为目标,适用于 Web 服务、B/S 系统。在 JDK 14 中被移除。

    • 示例:

      bash 复制代码
      # 使用CMS老年代收集器
      -XX:+UseConcMarkSweepGC
      
      # 使用ParNew新生代收集器
      -XX:+UseParNewGC
      
      # CMS后台线程数
      -XX:ConcGCThreads=2
      
      # CMS触发百分比
      -XX:CMSInitiatingOccupancyFraction=75
      
      # 开启CMS压缩
      -XX:+UseCMSCompactAtFullCollection
      
      # CMS FullGC前进行压缩
      -XX:CMSFullGCsBeforeCompaction=0
  3. G1 收集器(推荐)

    • 参数:-XX:+UseG1GC

    • 说明:JDK 9 及以后的默认收集器,面向服务端、大内存、低延迟应用。它将堆划分为多个 Region,并优先回收垃圾最多的 Region。

    • 示例:

      bash 复制代码
      # 使用G1垃圾收集器
      -XX:+UseG1GC
      
      # 设置最大GC暂停时间目标
      -XX:MaxGCPauseMillis=200
      
      # 设置区域大小
      -XX:G1HeapRegionSize=16m
      
      # 设置并发GC线程数
      -XX:ConcGCThreads=4
      
      # 设置混合GC周期收集中最大旧区域数量
      -XX:G1OldCSetRegionThresholdPercent=10
  4. ZGC 收集器(极致低延迟)

    • 参数:-XX:+UseZGC

    • 说明:新一代的低延迟垃圾收集器,目标是将停顿时间控制在 10ms 以下。适用于对延迟极其敏感的应用(如金融交易系统)。

    • 示例:

      bash 复制代码
      # 启用 ZGC
      -XX:+UseZGC
      
      # 调优参数
      -XX:ZAllocationSpikeTolerance=2.0  # 分配峰值容忍度
      -XX:ZCollectionInterval=300        # 强制GC间隔(秒)
      -XX:ZFragmentationLimit=10         # 碎片化限制
      -XX:ZProactive=true                # 启用主动GC
      
      # 内存映射调优
      -XX:ZUncommitDelay=300             # 内存未使用回收延迟(秒)
2.3 GC 日志相关

没有日志,调优就是盲人摸象。必须开启详细的 GC 日志。

  • 详细 GC 日志(JDK 8 及之前)

    bash 复制代码
    # 开启GC日志
    -XX:+PrintGC
    -XX:+PrintGCDetails
    -XX:+PrintGCTimeStamps
    -XX:+PrintGCDateStamps
    
    # 输出GC日志到文件
    -Xloggc:/path/to/gc.log
    
    # 开启GC日志轮转
    -XX:+UseGCLogFileRotation
    -XX:NumberOfGCLogFiles=5
    -XX:GCLogFileSize=10M
  • 统一日志框架(JDK 9+ 推荐)

    bash 复制代码
    # 详细 GC 日志
    -Xlog:gc*=debug:file=/app/logs/gc-%t.log:time,uptime,level,tags:filecount=10,filesize=100M
    
    # 选择性日志(生产环境推荐)
    -Xlog:gc=info:file=/app/logs/gc.log:time,uptimemillis,level,tags
    -Xlog:gc+heap=debug
    -Xlog:gc+ergo*=trace
    -Xlog:gc+age*=debug
  • 高级监控参数

    bash 复制代码
    # 打印命令行参数
    -XX:+PrintCommandLineFlags
    
    # 打印GC应用停顿时间
    -XX:+PrintGCApplicationStoppedTime
    
    # 打印GC前后堆信息
    -XX:+PrintHeapAtGC
    
    # 开启类加载日志
    -XX:+TraceClassLoading
    -XX:+TraceClassUnloading
    
    # 在发生 OOM 时自动生成堆转储文件,用于事后分析
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump.hprof
2.4 JIT 编译与性能优化
bash 复制代码
# 分层编译(JDK 8 默认)
-XX:+TieredCompilation

# 编译阈值调优
-XX:Tier0InvokeNotifyFreqLog=7
-XX:Tier2BackEdgeThreshold=100000
-XX:Tier3InvocationThreshold=2000
-XX:Tier3MinInvocationThreshold=100

# 代码缓存大小
-XX:ReservedCodeCacheSize=512m
-XX:InitialCodeCacheSize=64m

# 方法内联优化
-XX:MaxInlineSize=325
-XX:FreqInlineSize=325
-XX:InlineSmallCode=1000

三、不同场景的配置模板

3.1 开发环境配置
bash 复制代码
java -Xms512m -Xmx1g \
     -Xmn256m \
     -XX:MetaspaceSize=128m \
     -XX:MaxMetaspaceSize=256m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -XX:+PrintGC \
     -XX:+PrintGCDateStamps \
     -Xloggc:./logs/gc.log \
     -jar app.jar
3.2 测试环境配置
bash 复制代码
java -Xms2g -Xmx4g \
     -Xmn1g \
     -XX:MetaspaceSize=256m \
     -XX:MaxMetaspaceSize=512m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=150 \
     -XX:ParallelGCThreads=4 \
     -XX:ConcGCThreads=2 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=./dumps/ \
     -XX:+PrintGCDetails \
     -XX:+PrintGCDateStamps \
     -Xloggc:./logs/gc.log \
     -jar app.jar
3.3 生产环境配置
bash 复制代码
java -Xms4g -Xmx8g \
     -Xmn2g \
     -XX:MetaspaceSize=512m \
     -XX:MaxMetaspaceSize=1g \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=100 \
     -XX:ParallelGCThreads=8 \
     -XX:ConcGCThreads=4 \
     -XX:G1ReservePercent=15 \
     -XX:InitiatingHeapOccupancyPercent=35 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/data/dumps/ \
     -XX:+PrintGCDetails \
     -XX:+PrintGCDateStamps \
     -XX:+UseGCLogFileRotation \
     -XX:NumberOfGCLogFiles=10 \
     -XX:GCLogFileSize=10M \
     -Xloggc:/data/logs/gc.log \
     -jar app.jar
3.4 Docker容器配置
bash 复制代码
FROM openjdk:8-jre

# 设置JVM参数
ENV JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseG1GC"

# 使用容器感知的JVM
ENV JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"

CMD java $JAVA_OPTS -jar app.jar

四、实战调优示例

4.1 电商交易系统调优案例

场景特点:

  • 高并发、低延迟要求
  • 内存使用模式:中等对象分配率,长生命周期对象较多
  • 峰值 QPS:10,000+
  • 服务器配置:16核32G

调优方案:

bash 复制代码
#!/bin/bash
java -server \
  # 堆内存设置
  -Xms16g -Xmx16g \
  -Xmn8g \
  -XX:SurvivorRatio=8 \
  -XX:MaxTenuringThreshold=10 \
  
  # G1 收集器调优
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=100 \
  -XX:InitiatingHeapOccupancyPercent=40 \
  -XX:G1HeapRegionSize=16m \
  -XX:G1ReservePercent=15 \
  -XX:G1MixedGCCountTarget=16 \
  
  # 元空间与栈
  -XX:MetaspaceSize=512m \
  -XX:MaxMetaspaceSize=512m \
  -Xss512k \
  
  # 性能优化
  -XX:+TieredCompilation \
  -XX:ReservedCodeCacheSize=512m \
  -XX:+UseStringDeduplication \
  
  # 监控与诊断
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/app/logs/heapdump.hprof \
  -Xlog:gc=info:file=/app/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -XX:ErrorFile=/app/logs/hs_err_pid%p.log \
  
  # 应用JAR
  -jar ecommerce-app.jar
4.2 大数据处理应用调优

场景特点:

  • 高吞吐量要求,延迟不敏感
  • 大内存使用,频繁创建临时对象
  • 批量数据处理

调优方案:

bash 复制代码
#!/bin/bash
java -server \
  # 大堆内存设置
  -Xms32g -Xmx32g \
  -Xmn24g \
  -XX:SurvivorRatio=6 \
  
  # 并行收集器(吞吐量优先)
  -XX:+UseParallelGC \
  -XX:+UseParallelOldGC \
  -XX:ParallelGCThreads=16 \
  -XX:GCTimeRatio=99 \
  -XX:MaxGCPauseMillis=500 \
  
  # 大对象处理
  -XX:PretenureSizeThreshold=2m \
  -XX:+AlwaysPreTouch \
  
  # 性能优化
  -XX:+UseLargePages \
  -XX:LargePageSizeInBytes=2m \
  -XX:+UseCompressedOops \
  -XX:+UseCompressedClassPointers \
  
  # 监控配置
  -Xlog:gc*=info:file=/app/logs/gc.log:time,uptime,level,tags \
  -XX:NativeMemoryTracking=detail \
  
  -jar data-processing-app.jar

五、监控与诊断工具实战

5.1 命令行工具使用示例
bash 复制代码
# 1. 实时监控 GC 情况
jstat -gc <pid> 1s

# 2. 查看堆内存摘要
jmap -heap <pid>

# 3. 生成堆转储
jmap -dump:live,format=b,file=heap.hprof <pid>

# 4. 查看类加载统计
jstat -class <pid> 1s

# 5. 线程分析
jstack <pid> > thread_dump.txt

# 6. 原生内存跟踪
jcmd <pid> VM.native_memory summary

# 7. 全面的诊断信息
jcmd <pid> PerfCounter.print
5.2 自动化监控脚本
bash 复制代码
#!/bin/bash
# monitor_jvm.sh

PID=$1
LOG_DIR="/app/monitor"
INTERVAL=30

while true; do
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    
    # GC 统计
    jstat -gc $PID > $LOG_DIR/gc_$TIMESTAMP.log 2>&1
    
    # 线程转储(每5分钟一次)
    if [ $(($(date +%s) % 300)) -eq 0 ]; then
        jstack $PID > $LOG_DIR/thread_dump_$TIMESTAMP.log 2>&1
    fi
    
    # 内存使用
    ps -p $PID -o pid,ppid,pmem,pcpu,rsz,vsz,comm >> $LOG_DIR/memory_$TIMESTAMP.log 2>&1
    
    sleep $INTERVAL
done
相关推荐
馨谙4 小时前
SELinux 文件上下文管理详解:从基础到实战
jvm·数据库·oracle
cherry52308 小时前
Java大厂面试真题:Spring Boot + 微服务 + 缓存架构三轮技术拷问实录
jvm·spring boot·mysql·微服务·java面试·分布式架构·redis缓存
Han.miracle9 小时前
Java的多线程——多线程(3)线程安全
java·开发语言·jvm·学习·安全·线程·多线程
1.01^10001 天前
[5-01-01].第04节:初识字节码文件 - 字节码文件作用
jvm
找不到、了1 天前
JVM核心知识整理《1》
jvm
L.EscaRC1 天前
面向 Spring Boot 的 JVM 深度解析
jvm·spring boot·后端
学到头秃的suhian2 天前
JVM-类加载机制
java·jvm
NEFU AB-IN2 天前
Prompt Gen Desktop 管理和迭代你的 Prompt!
java·jvm·prompt
唐古乌梁海2 天前
【Java】JVM 内存区域划分
java·开发语言·jvm