Arthas实战:Java应用启动卡死,90%人踩错JVM参数顺序引发FullGC风暴
前言
近期线上订单业务系统频繁出现启动卡死、服务长时间无法就绪问题,进程存活但健康检查不通过、所有接口无响应。一开始怀疑代码死锁、数据库连接阻塞,借助Arthas线上诊断工具排查后,发现根源是一个极易忽略的启动参数书写错误:JVM参数放置在-jar之后导致全部失效,堆内存不足引发持续FullGC风暴。
本文完整记录故障现象、排查流程、根因定位、修复方案、校验手段与线上规范总结,附带全套可直接复制的Arthas排查命令。
一、故障现象
- 执行启动命令后服务长时间卡在初始化流程,服务器健康探测持续失败;
- 进程未崩溃、无OOM日志,但服务完全无法对外提供服务;
- 多次重启故障复现,服务器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
问题分析:
- JVM未识别手动配置的16G堆,使用系统默认堆上限仅6.8G;
- 默认Parallel GC收集器,无低停顿优化,FullGC上千次,累计STW停顿数十分钟;
- 堆内存持续高位,服务绝大多数时间都在执行垃圾回收,直观表现为启动卡死。
三、根因定位: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,关键指标全部恢复正常:
-
JVM参数全部生效
INPUT-ARGUMENTS完整展示所有内存、GC、SkyWalking探针配置;堆内存init=16G、max=16G,内存分配完整。 -
GC收集器切换为G1,无FullGC
G1 Young Generation
collectionCount : 11
collectionTime : 342
G1 Old Generation
collectionCount : 0
collectionTime : 0
- 仅少量新生代轻量GC,单次平均停顿31ms,远低于150ms停顿阈值;
- 全程无老年代FullGC,不再出现长时间STW阻塞。
- 配套资源指标全部健康
- 当前堆使用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. 高频踩坑点
- JVM参数、探针、
-D系统变量写在-jar后,参数完全失效; - shell脚本换行转义错误、容器托管脚本未更新,导致线上进程使用旧启动命令;
- 只配置堆大小,不使用G1低停顿收集器,Parallel GC FullGC停顿极长;
- 故障只看应用日志,未通过Arthas校验真实生效的JVM配置。
2. 线上启动脚本规范
- 严格遵循参数顺序:JVM参数 →
-javaagent→-D系统变量 →-jar→ Spring参数; - JVM参数上线后,必须通过
ps -ef | grep java或 Arthasjvm校验是否生效; - 生产统一启用G1GC,配置最大停顿时间,开启OOM自动堆转储;
- 容器部署,核对容器内存limit,避免容器上限限制堆内存。
3. 两类启动卡死快速区分
| 故障类型 | Arthas特征 | 根因 |
|---|---|---|
| GC风暴卡死 | gc命令FullGC持续上涨,堆内存持续打满 |
JVM堆不足、GC参数失效 |
| 业务阻塞卡死 | GC指标平稳,主线程栈卡在DB/Redis/Bean初始化/第三方接口 | 网络阻塞、初始化批量加载数据 |
七、结语
本次线上故障并非业务代码bug,仅仅是启动命令书写不规范,却直接导致服务完全不可用。Arthas作为Java线上诊断利器,仅靠jvm、gc两条基础命令就能快速定位GC类故障,建议所有Java开发、运维人员熟练掌握。
线上变更JVM参数后,一定要通过工具校验真实运行状态,避免低级配置错误引发线上事故。
更多推荐
所有评论(0)