JVM调优篇

章四 JVM调优

介绍JVM调优内容,JVM 调优是为了解决性能瓶颈、优化资源利用和提高系统吞吐量的重要手段。调优主要围绕 内存管理、垃圾回收、线程并发 和 启动性能 等方面展开

调优目标

  1. 响应时间:尽量降低延迟,减少GC停顿时间
  2. 吞吐量:尽量提高系统处理能力,减少GC时间占总时间的比例
  3. 内存使用:优化堆、栈等内存分配,避免内存泄漏和内存溢出

基本流程

  1. 明确调优目标:响应时间、吞吐量或内存占用
  2. 收集基线数据:通过监控工具(如JConsole、VisualVM等)收集cpu、内存、GC日志、线程状态等信息
  3. 发现瓶颈:分析性能问题来源,如GC频繁、线程阻塞和内存不足
  4. 调整JVM参数:逐步调整JVM启动参数,验证效果
  5. 监控和迭代:持续观察调整后的参数,逐步优化

常见参数

  1. 堆内存设置
参数含义
-Xms设置堆的初始大小,建议设置与-Xmx相等,避免动态扩展带来的开销
-Xmx设置堆的最大大小
-XX:NewRatio设置新生代、老年代的比例(默认1:2)
-XX:SurvivorRatio设置 eden和 Survivor比例(默认8:1)
-XX:MaxMetaspaceSize设置元空间最大大小
```
对于响应时间敏感应用,可以设置较大堆内存,减少GC次数
对于吞吐量优先应用,适当增大新生代,减少对象晋升的频率,减少Minor Gc次数
```
2. 垃圾回收器设置
GC 类型参数jdk版本适用场景特点
SerialGc(串行GC)-XX:+UseSerialGCJDK 1.3+单线程、低内存场景,例如简单桌面应用畅叙单线程回收,新生代使用复制算法,老年代使用标记-压缩算法,停顿时间较长,不适合高并发场景
ParallelGc(并发GC)-XX:+UseParallelGCjdk 1.4+(jdk8及以前默认GC)高吞吐量应用多线程回收,吞吐量优先;新生代使用复制算法,老年代使用标记-压缩算法
CMS GC-XX:+UseConcMarkSweepGCjdk 1.4+(jdk15之后已废弃)响应时间优先场景,例如在线交易、金融系统并发标记清除,降低老年代回收停顿时间;存在碎片化问题,容易导致Full GC;JKD9之后被G1替代,JDK14后废弃
G1 GC-XX:+UseG1GCjdk 7+(jdk9之后作为默认GC)大内存(>4GB)、低延迟场景,如试试推荐、游戏服务等面相区域分代收集,避免老年代停顿;支持用户指定最大停顿时间目标,平衡吞吐量和响应时间
ZGC-XX:+UseZGCjdk 11+超低延迟场景,例如高频交易,实时系统停顿时间<10ms,面向超大堆(支持TB级内存);使用染色指针技术避免全局暂停;与G1、CMS相比进一步降低停顿时间
Shenandoah GC-XX:+UseShenandoahGCjdk 12+超低延迟场景,适合低延迟的业务系统,如实时数据处理并发标记-压缩算法,减少老年代碎片化;与ZGC类似,提供更短的停顿时间,堆内存压缩性能更新,比ZGC更适合小型堆(<1TB)
Epsilon GC-XX:+UseEpsilonGCjdk 11+开发和测试场景,无垃圾回收需求,如临时程序或压力测试无垃圾回收起,禁用垃圾回收仅分配内存,内存耗尽直接抛出oom异常
小型应用或单线程任务,使用Serial GC
高吞吐量要求,使用Parallel GC
低延迟应用,优先考虑G1 GC,如果延迟要求极低,选择ZGC或Shenandoah GC
测试或无需回收,使用Epsilon GC
  1. GC日志参数
参数含义
-Xlog:gc启用GC日志输出(jdk>8)
-Xlog:gc*:file=gc.log将GC日志写入文件
-XX:+PrintGCDetails打印详细的GC日志(jdk<= 8)
-XX:+PrintGCApplicationStoppedTime打印程序因GC停顿的时间
-XX:+PrintTenuringDistribution打印对象在新生代中的晋升年龄分布
4. 线程栈大小
参数含义
-Xss设置线程栈大小
```
如果线程数较多,适当减少栈大小
如果线程需要深度递归,增大栈大小
```
5. JIT优化参数
参数含义
-XX:+TieredCompilation启用混合编译(默认开启)
-XX:CompileThreshold设置JIT热点编译出发阈值(默认10000次)
-XX:+PrintCompilation打印JIT编译的详细信息

优化示例

电商大促期间Full GC频繁案例

问题现象:
  • 大促期间每5分钟发生一次Full GC
  • 每次Full GC停顿2秒以上
  • 年轻代GC频率正常(10秒/次)
分析过程:
# 获取GC日志
jstat -gcutil 18923 1000 10

# 堆内存dump
jmap -dump:live,format=b,file=heap.hprof 18923
诊断结果:
pie
    title 堆内存分布
    "订单缓存对象" : 65
    "会话信息" : 20
    "临时DTO对象" : 10
    "其他" : 5
优化方案:
# 原JVM参数
-Xmx4g -Xms4g -XX:+UseParallelGC

# 优化后参数
-Xmx8g -Xms8g 
-XX:+UseG1GC 
-XX:MaxGCPauseMillis=200 
-XX:InitiatingHeapOccupancyPercent=45
-XX:G1ReservePercent=15
+ 添加本地缓存失效策略
优化效果:
指标优化前优化后
Full GC频率5分钟/次无Full GC
平均响应时间350ms120ms
吞吐量1200TPS4500TPS

大数据处理OOM问题

问题场景:
  • 每日处理千万级数据时发生OOM
  • 错误信息:java.lang.OutOfMemoryError: Java heap space
分析工具:
# 内存对象分析
jmap -histo:live 28471 | head -20

# 堆外内存检查
jcmd 28471 VM.native_memory
内存泄漏定位:
// 问题代码片段
List<DataRecord> buffer = new ArrayList<>();
while((record = readNext()) != null) {
    buffer.add(record); // 未限制缓冲区大小
    process(buffer);
}
优化方案:
  1. 增加批处理限制

    List<DataRecord> buffer = new ArrayList<>(1000);
    while((record = readNext()) != null) {
     buffer.add(record);
     if(buffer.size() >= 1000) {
         process(buffer);
         buffer.clear();
     }
    }
  2. JVM参数调整 ```diff

-XX:+UseCompressedOops +XX:MaxDirectMemorySize=512m -Xmx12g -Xms12g -XX:+UseG1GC -XX:SurvivorRatio=8


#### 优化效果:
- 内存使用稳定在8GB以内
- 处理速度提升30%
- 无OOM发生

### 高并发服务GC调优

#### 系统特征:
- 100+节点微服务集群
- 平均QPS 2万+/节点
- 响应时间波动大(50ms~2s)

#### GC日志分析:
```log
[GC pause (G1 Evacuation Pause) (young), 0.2308990 secs]
   [Parallel Time: 220.3 ms, GC Workers: 16]
      [Ext Root Scanning (ms): 45.6]
      [Update RS (ms): 32.1]
        [Processed Buffers: 1200]
      [Scan RS (ms): 12.4]
      [Code Root Scanning (ms): 1.2]
      [Object Copy (ms): 128.7]
      [Termination (ms): 0.3]
调优步骤:
  1. 调整Region大小 ```diff

-XX:G1HeapRegionSize=4m +XX:G1HeapRegionSize=8m


2. 优化并行阶段
```diff
-XX:ConcGCThreads=4 
+XX:ConcGCThreads=8
-XX:ParallelGCThreads=16
+XX:ParallelGCThreads=24
  1. 内存分配优化 ```diff

-XX:+UseTLAB +XX:TLABSize=128k -XX:-ResizeTLAB +XX:+ResizeTLAB


#### 效果对比:
| 参数               | 优化前停顿 | 优化后停顿 |
|------------------|-------|-------|
| Young GC         | 230ms | 150ms |
| Mixed GC         | 450ms | 280ms |
| 吞吐量损失           | 12%   | 6%    |

### 内存泄漏排查案例

#### 现象描述:
- 服务运行3天后内存占用达95%
- 重启后内存增长曲线异常

#### 排查工具组合:
1. **即时分析**:
```shell
jcmd 38742 GC.class_histogram | grep -v "java.lang"
  1. 趋势监控

    jstat -gcutil 38742 60s
  2. 堆转储分析

    // 使用MAT分析找到的泄漏对象
    Dominator Tree -> Thread Local Storage
泄漏代码定位:
// 错误使用ThreadLocal
private static ThreadLocal<SimpleDateFormat> formatter = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

// 正确写法
private static final ThreadLocal<SimpleDateFormat> formatter = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
修复效果:
graph LR
    A[运行时间] --> B[内存占用]
    A1(24小时) --> B1(45%)
    A2(48小时) --> B2(48%)
    A3(72小时) --> B3(50%)

ZGC低延迟场景实践

业务需求:
  • 金融交易系统
  • 要求单笔交易延迟<10ms
  • 99.99%的GC停顿<1ms
基准测试:
# 使用SPECjbb2015测试
java -XX:+UseZGC -Xmx16g -Xms16g \
     -XX:ConcGCThreads=8 \
     -XX:NativeMemoryTracking=detail \
     -jar specjbb2015.jar
参数调优:
-XX:+UseZGC
-Xms24g -Xmx24g
+XX:ConcGCThreads=6
+XX:SoftMaxHeapSize=20g
+XX:AllocatePrefetchStyle=1
+XX:-UsePerCPUMembank
性能指标:
指标Parallel GCG1 GCZGC
最大停顿时间1200ms450ms0.8ms
吞吐量损失8%12%4%
内存占用18GB16GB20GB
99.9%延迟35ms25ms8ms

容器环境调优实践

典型问题:
  • Kubernetes Pod频繁OOMKilled
  • 容器内存限制8GB,但实际使用超限
关键配置:
# 容器规范
resources:
  limits:
    memory: "8Gi"
    cpu: "4"

# JVM参数优化
-XX:+UseContainerSupport
+XX:MaxRAMPercentage=75.0
+XX:ActiveProcessorCount=4
-XX:+AlwaysPreTouch
内存分布优化:
pie
    title 容器内存分配
    "JVM堆" : 6144
    "堆外内存" : 1024
    "元空间" : 256
    "系统预留" : 512
监控方案:
# 容器内存监控
kubectl top pod

# JVM内部监控
jcmd 1 VM.native_memory summary
优化效果:
  • 内存使用稳定在7.5GB以内
  • 无OOMKilled发生
  • GC频率降低40%

    本文由博客一文多发平台 OpenWrite 发布!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值