开发者
资源
Java 服务迁移鲲鹏后 GC 停顿排查:从 JIT 热点分析到毕昇 JDK 适配
Java 服务迁移鲲鹏后 GC 停顿排查:从 JIT 热点分析到毕昇 JDK 适配
原创
发表于06/04
2200

一、现象与告警

业务 Java 服务从 x86 迁移到鲲鹏 920 后,Prometheus 告警频繁触发:jvm_gc_pause_seconds_max 持续超过 500ms 阈值,部分请求超时率达到 2%。监控面板显示 GC 频率从每分钟 3 次升至每分钟 8 次,Full GC 间隔从 4 小时缩短至 40 分钟。

业务侧表现为:订单接口 P99 延迟从 50ms 飙升至 320ms,定时任务执行时间翻倍。初步排除内存泄漏(堆使用稳定在 4GB),问题集中在 GC 停顿时间过长。


二、环境信息

项目x86 原环境鲲鹏迁移后环境
CPUIntel Xeon 6248 × 2鲲鹏 920 × 2(64 核)
内存128GB DDR4256GB DDR4
JDKOpenJDK 11.0.20OpenJDK 11.0.20(同版本)
GC 策略G1GCG1GC(参数未修改)
JVM 堆-Xmx4g -Xms4g-Xmx4g -Xms4g
框架Spring Boot 2.7.xSpring Boot 2.7.x(未变更)
监控Prometheus + GrafanaPrometheus + Grafana

三、定位与排查

阶段一:GC 日志初步分析

开启 GC 详细日志,定位停顿发生的位置:

# JVM 启动参数追加 GC 日志
-Xlog:gc*=info:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M

分析 GC 日志发现,停顿集中在 G1 Mixed GC 阶段,单次停顿 400-800ms。to-space exhausted 出现频率较高,说明存活对象复制效率低。

# 提取 GC 停顿超过 200ms 的记录
grep "Pause.*Mixed" /var/log/app/gc.log | grep -E "Pause.*[2-9][0-9]{2}\." | head -20

阶段二:JIT 编译热点分析

使用 jstat 观察 JIT 编译情况,发现鲲鹏上 JIT 编译耗时明显增加:

jstat -printcompilation <pid> 1000 10
# 输出显示 CompileQueue 中积压大量任务,编译线程长时间占用 CPU

进一步用 perf 抓取热点函数:

perf record -F 99 -p <pid> -g -- sleep 30
perf report --stdio | head -40

发现 c2_CompilerThread 占用 15% CPU,且编译生成的机器码未充分利用 ARM NEON 指令。OpenJDK 11 的 JIT 编译器(C2)对 ARM64 的优化路径不完善,部分热点方法生成的代码效率低于 x86。

阶段三:毕昇 JDK 验证

毕昇 JDK(BiSheng JDK)是华为针对鲲鹏优化的 OpenJDK 发行版,内置 ARM 微架构感知的 JIT 优化。下载并替换验证:

# 下载毕昇 JDK 11
wget https://mirrors.huaweicloud.com/kunpeng/archive/bisheng_jdk/bisheng-jdk-11.0.20-linux-aarch64.tar.gz
tar xzf bisheng-jdk-11.0.20-linux-aarch64.tar.gz -C /usr/local/

# 修改环境变量
export JAVA_HOME=/usr/local/bisheng-jdk-11.0.20
export PATH=$JAVA_HOME/bin:$PATH

# 验证版本
java -version
# 输出: openjdk version "11.0.20" (Bisheng)

四、解决方案与验证

第一步:切换毕昇 JDK 并调整 GC 参数

# systemd service 文件修改
[Service]
Environment="JAVA_HOME=/usr/local/bisheng-jdk-11.0.20"
ExecStart=/usr/local/bisheng-jdk-11.0.20/bin/java \
    -Xmx4g -Xms4g \
    -XX:+UseG1GC \
    -XX:MaxGCPauseMillis=200 \
    -XX:G1HeapRegionSize=8m \
    -XX:InitiatingHeapOccupancyPercent=35 \
    -XX:G1MixedGCCountTarget=16 \
    -XX:+UseStringDeduplication \
    -XX:+ParallelRefProcEnabled \
    -jar /opt/app/service.jar

关键参数说明:

  • G1HeapRegionSize=8m:鲲鹏内存带宽较高,增大 Region 减少 Region 数量,降低管理开销
  • InitiatingHeapOccupancyPercent=35:提前触发并发标记,避免 Mixed GC 时存活对象过多
  • G1MixedGCCountTarget=16:增加 Mixed GC 轮次,分摊复制开销

第二步:NUMA 绑核启动

# Java 进程绑定到 NUMA node 0,避免跨节点内存访问
numactl --cpunodebind=0 --membind=0 \
    /usr/local/bisheng-jdk-11.0.20/bin/java \
    -Xmx4g -Xms4g \
    -XX:+UseG1GC \
    -XX:MaxGCPauseMillis=200 \
    -jar /opt/app/service.jar

第三步:验证效果

使用 jstatwrk 进行压测验证:

# 监控 GC 行为
jstat -gcutil <pid> 5000

# 压测接口
wrk -t8 -c200 -d120s --latency http://10.0.0.1:8080/api/order/query
指标x86 + OpenJDK鲲鹏 + OpenJDK鲲鹏 + 毕昇 JDK鲲鹏 + 毕昇 JDK + 调优
GC 最大停顿180ms720ms280ms150ms
GC 频率(次/分)3.28.14.53.0
Full GC 间隔4h40min2.5h5h
接口 P99 延迟50ms320ms95ms48ms
吞吐(QPS)2,8001,2002,1002,950

排查结论:

  1. JIT 编译器是主因:OpenJDK 11 的 C2 编译器对 ARM64 优化不足,热点方法生成的机器码效率低,导致 GC 期间对象复制缓慢。毕昇 JDK 内置鲲鹏微架构感知优化,编译产物效率显著提升。
  2. GC 参数需适配:x86 上的 GC 参数不能直接搬到鲲鹏,Region 大小和触发阈值需要根据 ARM64 内存特性重新调整。
  3. NUMA 影响不容忽视:Java 堆内存跨 NUMA 节点分配会增加 GC 扫描开销,绑核后 Full GC 间隔从 2.5h 延长至 5h。

生产建议:Java 服务迁移鲲鹏时,优先切换毕昝 JDK;GC 参数不要照搬 x86 配置,建议先用默认参数压测,再根据 GC 日志逐步调整;双路服务器务必配置 NUMA 绑核。

收藏举报
Level 1
0
帖子
0
粉丝
0
获赞