执行jcmd JFR.dump导致JVM停滞,Vert.x服务出现线程阻塞如何解决?
执行jcmd JFR.dump导致JVM停滞,Vert.x服务出现线程阻塞如何解决?
遇到这种JFR dump触发Vert.x事件循环阻塞的情况确实挺闹心的,我来帮你拆解下问题根源,再给几个实际可行的解决办法:
为啥JFR.dump会阻塞JVM?
其实JFR本身设计是低开销的,但默认的JFR.dump是同步执行的:当你执行这个命令时,JVM会短暂暂停部分线程以获取一致的快照状态,同时同步把录制数据写入指定文件。如果这两个步骤里任何一个耗时(比如磁盘I/O慢、要捕获的事件数据量大),就会牵连到对延迟极度敏感的Vert.x事件循环线程,超过2秒就会触发BlockedThreadChecker的告警。
具体解决思路
1. 改用异步dump(最直接的核心解决办法)
给JFR.dump加上async=true参数,让JVM在后台异步完成dump操作,jcmd命令会立即返回,不会阻塞当前线程。修改后的shell命令如下:
jcmd $PID JFR.dump name=jfr filename=$JFR_FILE_LOCATION async=true > /dev/null && log_message "JFR dump initiated successfully" || log_message "JFR dump initiation failed"
⚠️ 注意:异步dump的话不能立刻拷贝文件到S3,因为JVM还在后台写文件。你可以通过jcmd $PID JFR.check轮询dump状态,等它完成后再执行S3拷贝,比如:
# 发起异步dump jcmd $PID JFR.dump name=jfr filename=$JFR_FILE_LOCATION async=true > /dev/null # 轮询检查dump是否完成 while true; do JFR_RUNNING=$(jcmd $PID JFR.check | grep -c "Running") if [ $JFR_RUNNING -eq 0 ]; then log_message "JFR dump completed, starting S3 copy" # 这里执行S3拷贝逻辑 break fi sleep 1 done
2. 降低JFR dump的开销
如果异步dump还是有残留问题,可以调整JFR的录制和dump参数,减少需要处理的数据量:
- 启动JVM时用
lowoverhead配置文件,比默认的profile配置少很多高开销事件:java -XX:StartFlightRecording:disk=false,name=jfr,settings=lowoverhead -jar your-vertx-service.jar - dump时指定只保留最近的有效数据(比如你每5分钟dump一次,就只dump最近5分钟的内容):
jcmd $PID JFR.dump name=jfr filename=$JFR_FILE_LOCATION async=true duration=5m
3. 优化磁盘I/O(解决同步写入瓶颈)
如果你的EC2实例用的是普通EBS卷(比如gp2),磁盘写入速度可能成为隐性瓶颈。可以把JFR临时文件写到内存盘(tmpfs)里,生成完成后再拷贝到S3,完全避免磁盘I/O阻塞JVM:
# 先创建tmpfs挂载点(按需调整size,比如10G足够存多次dump) mkdir -p /tmp/jfr-temp mount -t tmpfs -o size=10G tmpfs /tmp/jfr-temp # 把JFR_FILE_LOCATION指向这个内存路径 JFR_FILE_LOCATION=/tmp/jfr-temp/$(date +%Y%m%d-%H%M%S).jfr
4. 排查脚本里的其他阻塞点
虽然你已经跳过了S3拷贝,但可以再确认下:
- 脚本里的curl调用是否会阻塞主线程?可以把curl放到后台执行,或者用异步方式获取EC2实例信息
- 确保
jcmd命令本身没有被shell的同步逻辑卡住,比如不要在主线程里执行额外的耗时操作
为啥之前的尝试没起作用?
- 跳过S3拷贝:问题根源是JFR.dump本身的同步操作,不是S3拷贝环节
- 换ZGC:ZGC主要解决GC停顿问题,而你的阻塞是JFR dump的线程暂停和I/O导致的,和GC机制无关
备注:内容来源于stack exchange,提问作者swpalmer
相关产品推荐
相关产品推荐

