ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JVM 线上排查实战(七):jps 看不到进程、jstack 报不允许的操作怎么办?attach 失败的六种情况实测

JVM 线上排查实战(七):jps 看不到进程、jstack 报不允许的操作怎么办?attach 失败的六种情况实测 这个系列「JVM 线上排查实战」第七篇。前六篇教的所有命令,都有一个前提:工具能连上那个进程。(一)先把 JVM 看清楚:进程、参数、默认值(二)CPU 飙高:找到那个线程(三)线程卡住:死锁、BLOCKED、线程池打满(四)内存:OOM 了先干什么(五)GC 日志:从 JDK 8 升 17,老启动参数会让进程直接起不来(六)JFR 飞行记录器:录一段现场下来慢慢看(七)工具连不上进程时怎么办—— 本篇这一篇把「连不上」的几种情况逐个构造出来,贴每一种的原始报错,并给出还能用什么。实测环境:CentOS 7.9.2009(内核3.10.0-1160.71.1.el7.x86_64),同机三个 JDK —— Oracle1.8.0_381(/opt/jdk8)、OpenJDK1.8.0_412(/usr/lib/jvm/java-1.8.0-openjdk)、Oracle17.0.8(/usr/java)。测试程序是一个死循环烧 CPU 不停分配对象的小程序。先说结论jps看不到,不代表进程有问题,更不代表你连不上它。jps靠的是/tmp/hsperfdata_用户/pid这个文件,而 attach 靠的是另一套东西 —— 三种情况下jps空着但jcmd pid照样能用换个用户就看不见了:appuser跑jps只看得到自己那一个,root 的三个进程一个都不显示普通用户 attach root 的进程,报的是「不允许的操作」,不是「找不到进程」进程被kill -STOP之后,jstack和jcmd在限时内都不返回(60 秒 / 30 秒,退出码 124),kill -CONT之后立刻恢复正常-XX:DisableAttachMechanism是最坑的一种:jps里还看得见这个进程,但所有 attach 工具全废跨版本 attach 要看工具走哪条路:jstack/jcmd(走 attach)两个方向都能用;jmap -heap(走 SA)跨版本直接抛异常jstack -F在 JDK 17 上已经没了,报错会告诉你改用jhsdb jstack最后一条后路是kill -3:线程栈打到进程自己的标准输出里,连 attach 被禁用时它都还能用1.jps看不到进程:三种原因 ✅1.1 你和进程不是同一个用户root 下看,四个 Java 进程都在:$ /usr/java/bin/jps -l 5842 JfrDemo 6706 jdk.jcmd/sun.tools.jps.Jps 6679 JfrDemo 5737 JfrDemo 5739 JfrDemo切到appuser(它自己跑了其中一个,pid 6679),同一条命令:$ su - appuser -c /usr/java/bin/jps -l 6679 JfrDemo 6727 jdk.jcmd/sun.tools.jps.Jpsroot 的三个进程连影子都没有。不是权限报错,是干脆不显示 —— 这也是「jps看不到进程」最常见的原因:你用普通账号登录,而服务是另一个账号起的。反过来root 能看到所有用户的(上面那个 6679 就是 appuser 的)。1.2/tmp下的 hsperfdata 文件被清掉了jps的数据来源是这个目录:$ ls /tmp/hsperfdata_root/ 5737 5739 5842一个文件对一个 pid。很多机器上有定时清理/tmp的任务,把它删掉之后:$ rm -f /tmp/hsperfdata_root/5739 $ /usr/java/bin/jps -l 5842 JfrDemo 6679 JfrDemo 5737 JfrDemo 6829 jdk.jcmd/sun.tools.jps.Jps5739 没了。但进程活得好好的,按 pid 直接用jcmd一点问题都没有:$ /usr/java/bin/jcmd 5739 VM.uptime 5739: 323.039 sjps空 ≠ 连不上。这一条能省掉很多无谓的折腾:从ps -ef | grep java里拿到 pid,照样往下查。1.3 启动参数里关掉了UsePerfData$ /usr/java/bin/java -XX:-UsePerfData -cp c17 JfrDemo这个进程(pid 6894)在jps里同样是不存在的:$ /usr/java/bin/jps -l | grep -c ^6894 0而jstack照样连:$ /usr/java/bin/jstack 6894 2026-09-23 22:47:46 Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.89-LTS-211 mixed mode, sharing):2.jstack报「不允许的操作」✅用appuser去 attach root 的进程:$ su - appuser -c /usr/java/bin/jstack 5739 5739: 不允许的操作(英文环境下是Operation not permitted。)反过来root 去 attach 普通用户的进程,可以:$ /usr/java/bin/jstack 6679 2026-09-23 22:47:16 Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.89-LTS-211 mixed mode, sharing):→attach 要么同用户,要么用 root。看到「不允许的操作」先看ps -ef | grep pid第一列是谁。3. 进程被暂停了:命令挂在那儿不返回 ✅kill -STOP把进程挂起(线上等价的情况是进程卡在不可中断状态、或者被调试器停住):$ kill -STOP 5842 $ timeout 60 /opt/jdk8/bin/jstack 5842 stop8.txt 21; echo jstack 退出码$? 输出 $(stat -c%s stop8.txt) 字节 jstack 退出码124 输出 0 字节124是timeout杀掉它的退出码 ——60 秒内jstack没有返回任何东西。换jcmd也一样:$ timeout 30 /opt/jdk8/bin/jcmd 5842 Thread.print stopc.txt 21; echo jcmd 退出码$? 输出 $(stat -c%s stopc.txt) 字节 jcmd 退出码124 输出 6 字节那 6 个字节只是它先打的一行5842:,后面什么都没有 ——看起来像是在输出,其实已经卡住了。恢复之后立刻就正常:$ kill -CONT 5842 $ timeout 30 /opt/jdk8/bin/jstack 5842 cont8.txt 21; echo 退出码$? 输出 $(stat -c%s cont8.txt) 字节 退出码0 输出 3836 字节attach 是要目标进程自己配合的(它要起一个 Attach Listener 线程来响应)。进程被停住 没人接你的电话。所以jstack敲下去半天没反应时,先看ps -o stat -p pid:T就是被停住了。4. 最坑的一种:jps看得见,但谁都连不上 ✅启动参数里带了-XX:DisableAttachMechanism(有些安全加固基线会要求加它):$ /usr/java/bin/java -XX:DisableAttachMechanism -cp c17 JfrDemojstack:$ /usr/java/bin/jstack 7138 7138: The VM does not support the attach mechanism [退出码1]jcmd:$ /usr/java/bin/jcmd 7138 Thread.print 7138: com.sun.tools.attach.AttachNotSupportedException: The VM does not support the attach mechanism at jdk.attach/sun.tools.attach.HotSpotAttachProvider.testAttachable(HotSpotAttachProvider.java:154) [退出码1]而jps里它还好端端地列着:$ /usr/java/bin/jps -l | grep -c ^7138 1这就是为什么不能拿jps判断工具能不能用:前面 1.2 / 1.3 是「jps看不见但连得上」,这里正好反过来,是「jps看得见但连不上」。这两件事测的根本不是同一样东西。这种情况下,下面第 6 节那条后路仍然有效。5. 跨版本 attach:能不能用,取决于工具走哪条路 ✅同一台机器上有 JDK 8 和 17 的进程,手边的工具却只有一个版本时:用谁的工具打谁的进程结果JDK 17jstackJDK 8 进程✅ 正常输出(Full thread dump … 25.381-b09)JDK 8jstackJDK 17 进程✅ 正常输出(Full thread dump … 17.0.89-LTS-211)JDK 17jcmdJDK 8 进程✅ 正常输出JDK 8jmap -heapJDK 17 进程❌ 抛异常jmap -heap那次的原文:$ /opt/jdk8/bin/jmap -heap 5739 Attaching to process ID 5739, please wait... Exception in thread main java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)分界在工具走哪条路:jstack/jcmd走的是 attach 协议(让目标进程自己打印),对版本宽容;jmap -heap走的是 Serviceability Agent,要按目标 JVM 的内存结构去解析,版本对不上就炸。这也解释了(四)里那个现象:jmap -heap在 17 上要换成jhsdb jmap --heap --pid。jstack -F在 JDK 17 上没了JDK 8 上-F还在(它走的也是 SA):$ /opt/jdk8/bin/jstack -F 5737 Attaching to process ID 5737, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.381-b09 Deadlock Detection:JDK 17:$ /usr/java/bin/jstack -F 5739 Error: -F option used Cannot connect to core dump or remote debug server. Use jhsdb jstack instead→17 上把jstack -F换成jhsdb jstack --pid pid。6. 最后那条后路:kill -3✅所有 attach 工具都用不了的时候,还有一个办法:给进程发SIGQUIT,JVM 会把线程栈打到自己的标准输出里。$ ls -la d17.log # 发信号前 17 字节 $ kill -3 5739 $ ls -la d17.log # 发信号后 7401 字节 $ grep -c Full thread dump d17.log 1内容和jstack是一样的东西:Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.89-LTS-211 mixed mode, sharing): Threads class SMR info: _java_thread_list0x00007fc8240028c0, length16, elements{连第 4 节那个 attach 被禁用的进程,kill -3照样拿得到:$ kill -3 7138 da.log 17 - 5534 字节 $ grep -c Full thread dump da.log 1⚠️ 两个前提:它打到的是进程的标准输出,也就是你启动脚本里重定向的那个文件(nohup.out、catalina.out、xxx.log)。如果启动时把 stdout 丢进了/dev/null,那这条路也断了 ——这是启动脚本该现在就去改的一件事kill -3不会杀死进程(SIGQUIT被 JVM 接管了),但别对kill -9抱同样的期待,那个是真杀7. 顺带说清 attach 到底靠什么文件 ✅attach 用的是/tmp下的一个 socket 文件,名字是.java_pidpid:$ ls /tmp/.java_pid* /tmp/.java_pid5737 /tmp/.java_pid5739 /tmp/.java_pid5842 /tmp/.java_pid6679 …它不看java.io.tmpdir。启动时指定-Djava.io.tmpdir/root/tmpx的进程(pid 7253),attach 一样成功:$ /usr/java/bin/jstack 7253 td_jstack.txt 21; echo 退出码$? 输出 $(stat -c%s td_jstack.txt) 字节 退出码0 输出 5446 字节而 socket 文件仍然落在/tmp,指定的那个目录是空的:$ ls -a /root/tmpx . ..顺带一个细节:/tmp下能看到早就退出的进程留下的.java_pid文件(实测里有.java_pid77783这种,进程早没了)。所以别拿/tmp/.java_pidpid在不在来判断进程是否健在,那只是个残留文件。8. 本篇速查现象先查什么还能用什么jps什么都不显示你和进程是不是同一个用户(ps -ef | grep java第一列)ps拿 pid →jcmd pid照样能用jps少了某个进程/tmp/hsperfdata_用户/下有没有那个 pid 文件;启动参数有没有-XX:-UsePerfData同上,按 pid 直接连pid: 不允许的操作进程属主是谁切成同一个用户,或用 root命令敲下去不返回ps -o stat -p pid是不是T(被 STOP)kill -CONT恢复后再查;或kill -3看日志The VM does not support the attach mechanism启动参数有没有-XX:DisableAttachMechanismkill -3,从进程日志里读栈手边 JDK 版本和进程对不上工具走 attach 还是 SAjstack/jcmd可以跨版本;jmap -heap不行jstack -F在 17 上报错—jhsdb jstack --pid pid什么工具都连不上启动脚本有没有把 stdout 丢掉kill -3 看进程自己的日志
返回列表