前言

近期线上订单业务系统频繁出现启动卡死、服务长时间无法就绪问题,进程存活但健康检查不通过、所有接口无响应。一开始怀疑代码死锁、数据库连接阻塞,借助Arthas线上诊断工具排查后,发现根源是一个极易忽略的启动参数书写错误:JVM参数放置在-jar之后导致全部失效,堆内存不足引发持续FullGC风暴。
本文完整记录故障现象、排查流程、根因定位、修复方案、校验手段与线上规范总结,附带全套可直接复制的Arthas排查命令。

一、故障现象

  1. 执行启动命令后服务长时间卡在初始化流程,服务器健康探测持续失败;
  2. 进程未崩溃、无OOM日志,但服务完全无法对外提供服务;
  3. 多次重启故障复现,服务器16核CPU、内存资源充足,无其他业务抢占资源。

二、使用Arthas初步排查

1. Arthas附着进程

# 下载arthas启动包
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 附着Java进程,输入应用PID回车
java -jar arthas-boot.jar

2. 执行jvm查看运行时信息,发现第一个异常

INPUT-ARGUMENTS                                       []

自定义配置的-Xms/-Xmx/-XX:+UseG1GC等JVM参数全部为空,未被JVM识别。

3. GC指标暴露严重FullGC风暴

PS Scavenge
collectionCount : 10851
collectionTime : 2366366

PS MarkSweep(FullGC)
collectionCount : 1074
collectionTime : 1646617

HEAP-MEMORY-USAGE
init : 492.0 MiB
used : 4.5 GiB
committed : 6.8 GiB
max : 6.8 GiB

问题分析:

  1. JVM未识别手动配置的16G堆,使用系统默认堆上限仅6.8G;
  2. 默认Parallel GC收集器,无低停顿优化,FullGC上千次,累计STW停顿数十分钟;
  3. 堆内存持续高位,服务绝大多数时间都在执行垃圾回收,直观表现为启动卡死。

三、根因定位:Java启动参数顺序规范

错误启动命令(故障版本)

java -jar /data/jar/biz-order-app-v4.3.19.jar -Xms16g -Xmx16g -XX:+UseG1GC --spring.profiles.active=prod

致命错误:
-X/-XX JVM参数、-D系统属性、-javaagent探针必须放在-jar前面;
-jar后方的参数只会被Spring Boot应用接收,JVM会直接忽略,所有内存、GC配置全部失效。

修正后标准启动命令

java \
-Xms16g \
-Xmx16g \
-Xss512k \
-XX:MetaspaceSize=512m \
-XX:MaxMetaspaceSize=1g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=150 \
-XX:G1HeapRegionSize=32m \
-XX:+ParallelRefProcEnabled \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/tmp/heapdump.hprof \
-javaagent:/data/jar/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=biz-order-app \
-Dskywalking.agent.instance_name=prd:xxx.xxx.xxx.xxx \
-Dserver.tomcat.max-threads=400 \
-Dspring.datasource.hikari.maximum-pool-size=50 \
-jar /data/jar/biz-order-app-v4.3.19.jar \
--spring.profiles.active=prod \
--spring.config.location=/data/conf/biz-order-app/application-prod.yml

参数顺序规则:
JVM参数-javaagent探针 → -D系统变量 → -jar包路径 → --spring应用参数

四、修复后Arthas校验结果(故障彻底解决)

重启应用后再次执行jvm,关键指标全部恢复正常:

  1. JVM参数全部生效
    INPUT-ARGUMENTS完整展示所有内存、GC、SkyWalking探针配置;堆内存init=16G、max=16G,内存分配完整。

  2. GC收集器切换为G1,无FullGC

G1 Young Generation
collectionCount : 11
collectionTime : 342

G1 Old Generation
collectionCount : 0
collectionTime : 0
  • 仅少量新生代轻量GC,单次平均停顿31ms,远低于150ms停顿阈值;
  • 全程无老年代FullGC,不再出现长时间STW阻塞。
  1. 配套资源指标全部健康
  • 当前堆使用5.0G,16G堆内存余量充足;
  • 元空间占用204M,远低于1G上限,类加载无压力;
  • 无死锁、线程数量稳定、服务器负载低、文件句柄正常。

五、Arthas GC&启动卡死全套排查命令

1. 实时监控GC,判断FullGC风险

# 每1秒刷新一次GC统计
gc -i 1000

判断标准:

  • G1 Old / PS MarkSweep计数持续上涨 → 内存不足/内存泄漏;
  • FullGC总耗时大于MinorGC,代表服务大部分时间处于STW停顿。

2. 查看各内存分区占用

memory

重点观察Old Gen老年代占用增速,预判内存溢出风险。

3. 导出堆快照定位大对象/内存泄漏

heapdump --live /tmp/app-heap.hprof

将hprof文件下载至本地,使用MAT工具分析内存占用TOP对象,排查启动预加载海量数据、静态集合内存泄漏等问题。

4. GC正常但启动缓慢,排查业务阻塞

# 打印主线程堆栈,定位Spring启动阻塞点
thread 1

# 追踪启动类完整执行流程,定位耗时逻辑
trace com.xxx.Application run --skipJDKMethod false

# 追踪Bean初始化流程,卡死在某个Bean创建时使用
trace org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory createBean

六、线上踩坑总结&强制规范

1. 高频踩坑点

  1. JVM参数、探针、-D系统变量写在-jar后,参数完全失效;
  2. shell脚本换行转义错误、容器托管脚本未更新,导致线上进程使用旧启动命令;
  3. 只配置堆大小,不使用G1低停顿收集器,Parallel GC FullGC停顿极长;
  4. 故障只看应用日志,未通过Arthas校验真实生效的JVM配置。

2. 线上启动脚本规范

  1. 严格遵循参数顺序:JVM参数 → -javaagent-D系统变量 → -jar → Spring参数;
  2. JVM参数上线后,必须通过 ps -ef | grep java 或 Arthas jvm 校验是否生效;
  3. 生产统一启用G1GC,配置最大停顿时间,开启OOM自动堆转储;
  4. 容器部署,核对容器内存limit,避免容器上限限制堆内存。

3. 两类启动卡死快速区分

故障类型 Arthas特征 根因
GC风暴卡死 gc命令FullGC持续上涨,堆内存持续打满 JVM堆不足、GC参数失效
业务阻塞卡死 GC指标平稳,主线程栈卡在DB/Redis/Bean初始化/第三方接口 网络阻塞、初始化批量加载数据

七、结语

本次线上故障并非业务代码bug,仅仅是启动命令书写不规范,却直接导致服务完全不可用。Arthas作为Java线上诊断利器,仅靠jvmgc两条基础命令就能快速定位GC类故障,建议所有Java开发、运维人员熟练掌握。
线上变更JVM参数后,一定要通过工具校验真实运行状态,避免低级配置错误引发线上事故。

更多推荐