尧图精选

Arthas启动找不到Java进程?从JVM attach机制到权限容器排查指南

🕒 发布时间:2026/10/1 21:53:56 📁 来源:尧图网络
你有没有过这样的瞬间线上服务告警了你想用 Arthas 快速看一眼方法调用、线程栈或 GC 情况于是满怀信心地敲下java -jar arthas-boot.jar。结果等了两秒终端上出现一行冷冰冰的提示Can not find java process. Try to pass pid in command line.。更诡异的是有时列表是空的有时明明ps -ef能看到那个 java 进程Arthas 却把它当空气。Arthas 作为国内使用率最高的 Java 在线诊断工具遇到“启动时无法获取 java 进程”几乎是每个人都会撞上的第一道坎。这个问题的根源并不在 Arthas 本身而在 JVM 的 attach 机制、系统权限、容器隔离甚至 JDK 版本之间的协同关系上。这篇文章把我这些年排查这个问题的思路、命令、踩坑记录整理出来希望能帮你尽快定位原因少走弯路。1. 别急着怪 Arthas先弄懂它“找进程”的底层机制1.1 attach 模型是“临时敲门”agent 模式是“提前入住”Arthas 有两种工作方式搞清楚它们是理解问题的前提。第一种是 attach 模式这也是 arthas-boot 默认的行为。Arthas 启动时通过 JDK 自带的 Attach API 去枚举本机 JVM 进程拿到 PID 列表后让你选择。选完之后Arthas 会 attach 到目标 JVM把 agent 注入进去再通过 telnet 或 websocket 跟你交互。整个过程像临时喊一个专家上门维修你得知道门牌号PID得让门卫放行attach 机制还得拿对钥匙系统权限和用户身份。第二种是 agent 模式在 JVM 启动前显式加上-javaagent:/path/to/arthas-agent.jar。应用一启动agent 就已经加载好了后续直接通过端口连接。这种模式下根本不存在“找进程”的问题因为你相当于在装修时就把诊断仪器预埋进了房子里。所以你要先搞清楚你现在用的是哪种方式如果是在应用启动后临时诊断基本都走 attach 模式“无法获取 java 进程”的报错也几乎都发生在 attach 模式下。1.2 报错常见的三种形态先对号入座同样是“无法获取 java 进程”实际报错形态不太一样。我把日常遇到的情况归成三类报错形态典型提示最可能的原因列表为空Can not find java process. Try to pass pid in command line.用户身份不一致、tmpdir 被修改、jps 本身也看不到选中进程后失败Attach to process 12345 failed, error: ...权限不足、容器 seccomp 限制、DisableAttachMechanism、tmpdir 不可写启动直接异常NoClassDefFoundError: com/sun/tools/attach/VirtualMachine运行 Arthas 的环境是 JRE缺 JDK 的 attach 模块第一类最容易让人困惑因为你会想明明服务器上有 java 进程凭什么说找不到第二类更贴近“进程看到了但进不去”。第三类就比较直白了属于环境本身不完整。先把报错对号入座接下来每一步排查才有方向。2. 第一道分水岭jps 能看到的Arthas 不一定列出jps 看不到的Arthas 一定找不到2.1 jps 能看到但列表为空版本与运行环境的锅更大遇到这种问题我第一反应不是查权限而是先跑一条命令jps -l如果jps -l能清楚列出目标 Java 进程而 Arthas 的列表却是空的那问题通常出在 Arthas 自身环境上比较常见的有两种第一种是 Arthas 版本太旧。早期版本在 JDK 9 的模块化环境下用 jps 或者 attach API 枚举进程时兼容性并不好列表偶尔会出现漏进程的情况。这种问题没什么好说的直接升级到最新版本或者至少 3.6.0 以上的稳定版。第二种是 Arthas 运行时用的 JDK 和目标任务不是同一个。服务器上装多个 JDK 的情况太常见了。java -jar arthas-boot.jar用的是 PATH 里默认的 java而目标进程可能跑在另一个 JDK 上。当两个 JDK 的 attach 协议或 hsperfdata 文件格式有差异时就可能出现“目标进程明明活着Arthas 却列不出来”的诡异现象。解决办法是显式指定 JAVA_HOMEexport JAVA_HOME/usr/local/jdk8 java -jar arthas-boot.jar如果你用的是as.sh脚本脚本本身会尝试自动探测也可以手动指定JAVA_HOME后再执行。还有一个容易被忽略的场景目标 Java 进程跑在 OpenJ9 或一些特殊 JVM 上。OpenJ9 对 hsperfdata 机制的支持和 HotSpot 不完全一样jps 偶尔会漏Arthas 枚举时也会跟着漏。这种情况下别耗在列表上直接用后面第 4 节的方法指定 PID 强连。2.2 连 jps 都看不到用户、tmpdir 和启动方式的盲区如果jps -l本身就看不到目标进程那就不是 Arthas 的锅了问题出在 JVM 进程可见性层面。我建议按下面的顺序逐一排查。先看用户身份。JVM 枚举进程时会去读取临时目录下的性能数据文件这个目录按用户名隔离通常是/tmp/hsperfdata_username。普通用户 A 看不到用户 B 启动的 Java 进程这在多账号服务器上非常常见。你拿 root 登录目标进程是 appuser 起的jps -l大概率看不到。验证方式很简单ps -ef | grep java | grep -v grep sudo -u appuser jps -lps看到的是操作系统层面的进程jps看到的是“当前用户视角下的 Java 进程”两者并不等价。ps能看到但jps看不到多半就是用户隔离。再看 tmpdir。如果目标进程启动时加了-Djava.io.tmpdir/data/tmp那么它的 hsperfdata 文件也会跑到/data/tmp下。jps 默认从/tmp找自然找不到。你可以用下面的方式验证ls -la /data/tmp/hsperfdata_* /tmp/hsperfdata_* 2/dev/null如果确认 tmpdir 被改了jps 可以用-J-Djava.io.tmpdir/data/tmp临时指定jps -J-Djava.io.tmpdir/data/tmp -l但这种“换个目录找”的思路在 Arthas 里不好直接落地所以更务实的做法是记住目标 PID直接走强连方案。另外还有一种情况进程是用javawWindows或某些服务包装器启动的它们可能不创建标准 hsperfdata 文件导致 jps 和 Arthas 都看不到。这类进程往往比较特殊排查时不要纠结列表直接指定 PID。3. 权限与容器的隐形边界为什么 root 反而连不上普通用户起的进程3.1 attach 的 UID 校验切到同一个用户再说很多人问我我都用 root 了为什么还连不上 appuser 启动的 Java 进程这里得说清楚一个关键点枚举进程和 attach 进程对权限的要求不一样。枚举阶段可能只是读文件root 有权限读所以能看到但 attach 阶段需要向目标 JVM 发信号、创建本地 socket 文件并建立通信HotSpot 的 attach 机制会严格校验调用方 UID 是否与目标进程 UID 一致。不一致就直接拒绝root 也不例外。所以在生产环境排障我基本不会用 root 去 attach 服务账号的进程而是先切用户sudo -u appuser java -jar arthas-boot.jar或者指定 PIDsudo -u appuser java -jar arthas-boot.jar 27654如果你不确定目标进程是哪个用户起的用一条命令确认ps -o user,pid,cmd -p 27654看到 user 那一列再用sudo -u 用户名去执行 Arthas大部分“找不到进程”的问题在这一步就能解决。macOS 上还有一层额外的坑。如果你是在本机调试Java 进程由图形界面会话启动而你在终端里用另一个用户身份运行 Arthasattach 会因为权限或 session 隔离失败。这种情况下直接 sudo 运行或者确保终端的登录用户和 Java 进程的启动用户一致。3.2 Docker/K8s 里的 seccomp 与 PID namespace 坑容器环境是重灾区而且报错花样最多。最常见的是docker exec进容器运行 arthas-boot能看到 Java 进程但一 attach 就报Attach to process ... failed。这个现象十有八九是 Docker 默认的 seccomp 配置把ptrace系统调用拦了。JVM 的 attach 机制在 Linux 上依赖和信号、socket 文件、进程跟踪相关的系统调用没有 SYS_PTRACE 权限就白搭。解决办法是在启动容器时放开权限docker run --cap-addSYS_PTRACE --security-opt seccompunconfined -it my-service如果你用 docker-compose可以这样写security_opt: - seccomp:unconfined cap_add: - SYS_PTRACE到 K8s 里对应的配置是 securityContextsecurityContext: capabilities: add: [SYS_PTRACE]另外一个坑是容器内只有 JRE 没有 JDK。很多基础镜像为了瘦身只装 JRE。Arthas 在 JDK 8 下依赖 tools.jar在 JDK 9 下依赖jdk.attach模块这些只有完整 JDK 才有。容器里跑 Arthas 时如果报类找不到、模块找不到先检查一下java -version和 JAVA_HOME 指到哪里。要么把 JDK 挂载进容器要么换一个自带 JDK 的诊断镜像。还有 PID namespace 的影响。你进入容器后看到的 PID 1跟宿主机上看到的 PID 可能是两回事。如果你在宿主机上运行 arthas-boot默认是看不到容器里的 Java 进程的反过来在容器里也看不到宿主机的进程。所以操作前先想清楚你现在的执行环境到底在哪个 namespace 里。如果确实需要在宿主机上诊断某个容器内的 Java 进程容器启动时要加--pidhost并且放开 ptrace 权限否则别费劲了直接进容器操作更省事。4. 绕过列表直接指定 PID 强连的完整姿势4.1 指定 PID 的各种启动参数组合列表获取有问题最直接的办法就是绕过列表手动指定 PID。Arthas 本来就支持这个姿势java -jar arthas-boot.jar 27654用as.sh也一样./as.sh 27654如果目标机器有多个网卡、多个 Java 进程或者你想固定端口方便防火墙放行可以把 telnet 端口和 http 端口一起指定java -jar arthas-boot.jar --telnet-port 3658 --http-port 8563 27654这里有个小细节PID 必须是目标 Java 进程的 PID不是容器 PID也不是调用方的 PID。如果你是在容器里操作执行jps -l看到的就是容器视角的 Java PID用它就行。指定 PID 还有一个好处省掉了交互选择的那一步在脚本化、自动化排障时非常有用。比如你想快速抓一下某个服务的线程栈直接写./as.sh 27654 -c thread -n 3这些话可以在非交互模式下执行配合报警系统做自动化诊断很合适。4.2 PID 也连不上时按这个顺序往下查指定 PID 仍然失败的场景我遇到过不少。这时候不要慌按下面的顺序逐项验证。第一步确认 attach 功能本身没被禁用。有些安全加固脚本会给 JVM 加上-XX:DisableAttachMechanism这个参数一加jstack、jmap、jcmd、Arthas 全部失效。验证方式jinfo -flag DisableAttachMechanism 27654如果输出是-XX:-DisableAttachMechanism说明没禁用如果输出是-XX:DisableAttachMechanism或命令本身报错那问题就锁定了。第二步用 jstack 做个基线测试。Arthas 本质上也是通过 Attach API 加载 agent所以 jstack 能不能用可以作为判断 attach 是否正常的快速探针jstack -l 27654如果 jstack 也失败说明问题在 JVM attach 层面不是 Arthas 的锅。如果 jstack 成功而 Arthas 失败那就回头查 Arthas 版本、缓存、日志。第三步检查 tmpdir 权限和磁盘空间。attach 需要在临时目录下创建.java_pidpidsocket 文件如果/tmp被写满或者权限被改得很死attach 一样会失败。可以看df -h /tmp ls -ld /tmp第四步翻 Arthas 的日志。Arthas 会在~/.arthas/logs下记录详细日志里面通常能看到 attach 失败的具体异常堆栈tail -n 100 ~/.arthas/logs/arthas.log日志里的异常信息非常直白比如well-known file is not secure指向权限问题Unable to open socket file指向 tmpdir 或用户问题。第五步清理 Arthas 缓存重试。Arthas 首次运行会从远程拉取依赖缓存在~/.arthas目录。缓存损坏也可能导致各种奇怪问题rm -rf ~/.arthas java -jar arthas-boot.jar 276544.3 终极兜底agent 模式与提前埋点如果 attach 实在连不上而你又必须在这个进程上做诊断那就只剩一条路等待重启窗口在 JVM 启动参数里显式加上 Arthas agent。java -javaagent:/opt/arthas/arthas-agent.jar -jar your-app.jar加了之后应用启动时就会加载 Arthas agent随后你可以直接通过 telnet 连接 3658 端口。这种方式从源头绕开了“启动时找进程”的问题非常适合那种安全加固特别严格、禁止动态 attach 的环境。我见过不少团队把这一招固化到发布流程里如果服务对诊断能力是刚需就在启动脚本里默认带上 agent或者至少保留一个可切换的配置项。问题真正来临时你不需要在凌晨两点和 attach 权限搏斗直接连端口就行。不过要注意agent 模式也有成本Arthas agent 会占用一定内存和性能通常在低负载场景下问题不大但高并发核心链路要评估一下。我的建议是要么在预发环境加 agent 做充分压测要么平时不启用只在发布时通过环境变量动态注入。5. 一次真实排障复盘从空列表到成功 attach 的全过程5.1 场景描述与第一步命令去年有一次线上订单服务 CPU 飙到 90%我登录服务器打算用 Arthas 看线程。当时犯了一个典型错误直接用 root 执行了java -jar arthas-boot.jar结果列表空荡荡连自己差点都想骂人。我马上用ps确认目标进程还活着ps -ef | grep order-service输出里有目标 PID 18342进程用户是 orderuser。问题基本清晰了这是典型的用户隔离。5.2 逐步缩小范围的四个验证第一步切到目标用户验证 jps 视角sudo -u orderuser jps -l输出里出现了 18342确认进程在 orderuser 的 JVM 视角下是可见的。第二步直接用目标用户运行 Arthassudo -u orderuser java -jar arthas-boot.jar 18342这次不再报“找不到进程”而是换了新错误Attach to process 18342 failed。说明问题从进程可见性转移到了 attach 权限层。第三步用 jstack 探测sudo -u orderuser jstack -l 18342结果也失败。这时候我已经怀疑启动参数里加了禁 attach 的标志。查一下进程完整启动参数ps -ef | grep 18342 | grep -v grep cat /proc/18342/cmdline | tr \0 果然看到了-XX:DisableAttachMechanism和-Djava.io.tmpdir/var/tmp两个关键参数。前者直接禁止了所有动态 attach后者把 hsperfdata 挪到了非默认位置。这两个参数叠加Arthas 想找到进程都难。第四步验证 tmpdir 的影响。用 jps 指定 tmpdir 再看sudo -u orderuser jps -J-Djava.io.tmpdir/var/tmp -l18342 确实在列表里说明 tmpdir 修改确实影响了枚举逻辑。5.3 最终结论与后续加固结论是这个进程被安全加固策略锁死了Arthas 在进程运行期间根本无法 attach。想现场诊断最靠谱的办法就是重启窗口内移除-XX:DisableAttachMechanism并在启动参数里显式加上 agent 埋点。我们没有为了诊断而立刻重启线上服务而是先用了 jstack 的替代手段绕过其实 jstack 也依赖 attach也不可用。最后我们改用实时日志和监控曲线先顶住等发布窗口把启动参数修正后再在脚本里预留了-javaagent:/opt/arthas/arthas-agent.jar下一次启动就自带诊断能力了。这个案例最有价值的教训是看似是 Arthas 的问题根源却是启动参数和账号体系。排查时不要围着 Arthas 转先看 JVM 层面的可见性和可 attach 性。6. 日常预防我踩过几次坑后固化的几条规矩6.1 上线前10秒能做完的三项自检与其每次都在线排障不如上线前顺手做几件很小的事真的能省下大把时间。第一别手痒加-XX:DisableAttachMechanism。有些安全扫描建议加但如果你们的运维和开发需要在线诊断这就是自己给自己上锁。如果必须要加请确定团队里有替代方案比如提前埋 agent。第二明确服务启动账号。在部署文档里写清楚“服务由 appuser 启动诊断命令必须用sudo -u appuser执行”。这一条能直接解决 80% 的“列表空白”问题。第三检查 tmpdir 是否有被改写的习惯。如果中间件或安全策略要求自定义java.io.tmpdir那就要知道它对 jps 和 Arthas 的影响。最好在部署脚本里加一条注释标注自定义 tmpdir 的路径排障时少走弯路。容器和 K8s 环境还要额外确认镜像里是否有完整 JDK以及 runtime 是否放开了 SYS_PTRACE。这些配置看起来是“安全收紧”但对 Java 诊断链路的破坏是致命的。6.2 线上紧急介入时的速查清单真到了线上出问题、手忙脚乱的时候有一个备查清单非常重要。我自己的习惯是按下面这个顺序来# 1. 确认目标进程和启动用户 ps -ef | grep java | grep -v grep # 2. 切到同一用户看 jps 视角 sudo -u user jps -l # 3. jps 看不到时检查 tmpdir 和 hsperfdata ls -la /tmp/hsperfdata_* /var/tmp/hsperfdata_* 2/dev/null # 4. 检查禁用 attach 参数 jinfo -flag DisableAttachMechanism pid # 5. 用 jstack 做 attach 基线测试 sudo -u user jstack -l pid # 6. 直接指定 PID 启动 Arthas sudo -u user java -jar arthas-boot.jar pid如果第 5 步 jstack 都失败就直接跳过 Arthas 的挣扎去查启动参数和容器权限。如果 jstack 成功但 Arthas 失败再去清理 Arthas 缓存、升级版本、翻日志。这套清单看起来简单但每次都能把问题边界划得很清楚到底是你“看不见进程”还是“看得到但进不去”。这两类的排查路径完全不同。最后分享一个个人心得Arthas 这类工具能不能用表面看是工具问题本质上是 JVM 生态的透明度和控制权问题。把账号、权限、tmpdir、容器能力这些底层约束想清楚你不仅能解决“启动找不到进程”还能举一反三应对 jstack、jmap、jcmd 连不上的各种衍生问题。把这些经验沉淀成团队的 trouble-shooting 手册比每次临时百度强太多了。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →