
1. 问题现象Arthas 启动时的经典翻车现场先说说我自己第一次遇到这个问题的经历。当时是在一台 CentOS 7 的测试服务器上排查一个 Spring Boot 服务的内存异常问题习惯性敲下java -jar arthas-boot.jar结果控制台直接给我来了一句[INFO] Can not find java process. Try to pass pid in command line.我那时候还愣了一下明明ps -ef | grep java能看到进程还活着怎么 Arthas 会找不到后来试了jps也提示process information unavailable。又试了加 PID 直接 attach 的方式结果报Unable to open socket file。那一整个下午我都在跟这个报错较劲后来陆陆续续在不同环境里又碰到过几次才算把这个问题彻底摸透。这篇文章就围绕这个场景展开把 Arthas 启动时找不到 Java 进程的各种原因、判断思路、解决手段一次性讲清楚。不管你是用 Arthas 做日常排查的 Java 开发还是负责线上环境维护的运维或 SRE只要你需要在服务器上面对 JVM 应用这篇文章都能帮你少踩几个坑。顺便说一句Arthas 这个工具本身非常好用阿里开源的 Java 诊断利器不修改代码、不重启服务就能在线排查问题包括方法调用追踪、热更新、内存快照分析等。但工具再好如果第一步连进程都 attach 不上去后面全是白搭。所以把启动阶段的坑填平是高效使用 Arthas 的前提。2. 核心原理Arthas 到底是怎么找到 Java 进程的要解决一个问题先得理解背后的机制。Arthas 启动时并不是漫无目的地去全盘扫描进程它的进程发现机制其实分成了好几层每一层都有各自的前提条件和短板。搞清楚这些你才知道报错信息到底在告诉你什么。2.1 报错信息的真实含义Can not find java process这句话很容易误导人它并不是说你的 Java 进程不存在而是说 Arthas 在它预设的查找逻辑里没有找到符合条件的进程。这就像你让朋友帮你在书架上找一本书朋友说没找到很可能不是书不在而是书放在了别的书架上或者书的封面上没有你描述的特征。Arthas 的查找逻辑大致是通过调用ps命令枚举系统进程筛选出名字里包含java的进程再进一步做 attach 操作。如果筛选结果为空或者筛选出的进程在 attach 时失败它就会报告这个经典的错误。所以当你看到这个报错时脑子里要立刻浮现出三个待排查方向进程本身是否存在、进程是否被正确识别、attach 动作是否被允许。下面拆开细说。2.2 Arthas 获取进程列表的两条路径Arthas 获取进程列表的方式本质上依赖 JVM 的 Attach 机制。它先通过系统级命令枚举所有进程再用 Java 自带的VirtualMachine.list()方法来获取可 attach 的 JVM 列表。这两条路分别对应不同的排查方向第一操作系统层面。Arthas 会执行类似于ps -ef | grep java的操作拿到机器上所有命令行里包含java的 PID然后展示在启动界面上。如果这个环节出问题通常表现为列表里什么都没有。第二JVM 层面。Java 的 Attach 机制要求目标 JVM 在启动时创建临时文件目录默认是/tmp/hsperfdata_用户名/里面存放 perf data 文件。Arthas 通过VirtualMachine.list()可以拿到这些 JVM 的实例信息。如果这个目录不存在或者 Arthas 进程没有权限读取就会出现能看到进程但 attach 失败的诡异情况。这里有个很容易被忽略的点Arthas 启动后输入的序号其实就是 JVM 实例的 PID。也就是说Arthas 把系统进程列表和 JVM 实例列表做了比对只有同时出现在两个列表里的进程才会被展示出来供你选择。2.3 导致找不到的五类典型原因根据我自己的实战经验以及身边同事踩过的坑Arthas 启动时无法获取 Java 进程的原因大体可以归为五类当前用户权限不足无法读取/tmp/hsperfdata_*目录或执行 attach 操作Java 进程是别人启动的运行用户和 Arthas 启动用户不一致目标 JVM 是容器内的进程宿主机上执行 Arthas 时看不到完整信息目标进程不是标准 Java 进程或者是被改名、被重定向了启动命令的进程Arthas 自身的依赖问题比如 fastjson 版本冲突导致启动流程中断后面的章节我会针对每一类原因给出具体的排查命令和解决方案。但先记住一个最重要的思路不要只盯着 Arthas 的报错信息本身要回到操作系统和 JVM 的层面去验证进程的真实状态。3. 动手排查我从实际踩坑中总结的五步确认法这一部分是最实用的内容。我把自己多次排查这个问题的流程整理成了一套标准化的步骤每遇到Arthas 找不到进程我就按这个顺序来基本上五分钟内能锁定问题根因。3.1 第一步确认 Java 进程到底还活着没有很基础但绝不废话的一步。不要想当然地认为服务还在就跑着很多情况下服务进程早就崩了只是你没注意到。而且要注意jar包部署的应用、java -version进程、IDEA 里的 JVM 进程在ps里都叫java得分辨清楚。先在目标机器上执行jps -lv这个命令会列出当前用户下所有 Java 进程的 PID 和启动参数-l显示完整主类名-v显示 JVM 参数。如果输出为空或者根本没有jps命令说明 JDK 环境变量没配好或者进程不是当前用户启动的。接着再用系统命令交叉验证ps -ef | grep java | grep -v grep如果ps能看到进程而jps看不到恭喜你问题大概率是用户权限或者 hsperfdata 目录权限导致的。如果ps都看不到那说明进程确实不存在问题变成了服务为什么挂了这就不在本文讨论了。实操心得我曾经遇到过一次很尴尬的情况ps能看到好几个 java 进程但我其实不知道哪个才是目标服务的 PID。后来靠jps -lv里 JVM 参数带的-Dspring.profiles.activetest才认出来。所以我的习惯是先跑jps -lv看启动参数认进程比单纯看 PID 靠谱得多。3.2 第二步验证 Attach 机制的基础设施是否正常JVM 的 Attach 机制依赖/tmp/hsperfdata_用户名目录。当我们执行jps或者启动 Arthas 时工具会去这个目录读取 JVM 的 perf data 文件。可以手动检查一下ls -la /tmp/hsperfdata_*正常情况下你应该能看到一个或多个以用户名命名的目录比如hsperfdata_root、hsperfdata_admin。目录里面是形如PID的文件没有扩展名。这个文件其实就是目标 JVM 运行时创建的 shared memory 映射文件Attach 机制靠它做进程间通信。如果这个目录不存在、权限不足、或者被系统定期清理了Arthas 就无法感知到对应的 JVM 实例。ls -la /tmp/hsperfdata_admin/输出类似这样total 0 drwxr-xr-x 2 admin admin 60 Mar 15 10:22 . drwxrwxrwt 8 root root 4096 Mar 15 10:22 .. -rw------- 1 admin admin 32768 Mar 15 10:22 23456看到这个 PID 文件存在说明 JVM 的 perf data 是正常的。如果这步正常但 Arthas 还是报错那问题多半出在权限上等下第三步会细说。有一点要特别提醒有些系统会配置systemd-tmpfiles-clean.timer定期清理/tmp目录或者 Docker 容器里容器的/tmp被映射到临时文件系统重启后目录就没了。我遇到过一次线上事故就是因为清理任务把旧的 hsperfdata 目录删了所有 Java 进程在那台机器上都无法被附加Arthas 彻底没法用只能重启应用来恢复。这类问题最棘手的就是重启前查不到重启后临时恢复了隐蔽性极强。3.3 第三步检查当前用户和目标进程的身份关系如果你的应用是 root 用户启动的而你当前登录的是普通用户直接在终端里执行java -jar arthas-boot.jar大概率会失败。原因很简单Attach 机制要求发起 attach 的用户和目标 JVM 的用户一致。JVM 创建的 hsperfdata 文件权限是-rw-------也就是说只有文件所有者才能读。普通用户去读 root 的 perf data 文件直接 Permission denied。解决办法有两种第一种切换成目标进程的启动用户去执行 Arthassudo su - admin java -jar arthas-boot.jar或者直接以 root 身份执行如果安全策略允许sudo java -jar arthas-boot.jar第二种使用--target-ip参数配合远程 attach这个是企业版功能不展开。社区版的标准做法就是切换用户。这里有个常见疑问为什么ps -ef | grep java能看到进程jps却看不到这正是因为ps是系统级的命令每个人都有权限读/proc下的进程列表但jps依赖 hsperfdata而普通用户读不了 root 的 hsperfdata。两者机制不同所以才有这种看得到但 attach 不了的矛盾现象。注意如果你是在公司生产环境千万别直接sudo执行不知名的 jar 工具。Arthas 虽然官方开源但出于安全考虑建议先在测试环境验证操作流程再在线上执行。而且操作前最好确认一下这个 arthas-boot.jar 是从官方渠道下载的哈希值核对一下再跑。3.4 第四步绕过自动检测直接指定 PIDArthas 启动时其实支持直接传 PID 参数跳过自动检测进程列表的步骤java -jar arthas-boot.jar 23456这里的23456是目标 Java 进程的 PID。这个方法更直接但有一个前提你必须知道准确的 PID而且当前用户依然要有权限 attach。如果直接指定 PID 后仍然报错写入日志的错误信息会更有针对性比如java.io.IOException: Connection refused通常是 attach socket 通信失败多半是权限问题com.sun.tools.attach.AttachNotSupportedException说明目标进程不是标准 Java 进程或者不是 HotSpot/OpenJ9 的 JVMUnable to open socket file说明 hsperfdata 里的 socket 文件不可读一般是用户在切换如su后未完整初始化时造成的有时候我们生产环境的 Java 进程是 root 用户起的但是我会用一个低权限账号去执行 Arthas 排查这时候就会非常纠结。其实还有一个比较隐蔽的操作使用sudo -u root执行 Arthas。但要注意sudo 时如果环境变量带了奇怪的 JAVA_HOME也会影响启动结果。实操心得直接传 PID 是跳过自动检测环节最有效的手段。但如果你连 PID 都不清楚可以先用下面的命令找到准确的 PIDpgrep -f java.*myapp.jar这会返回所有命令行里匹配myapp.jar的进程 PID。多个实例会有多个 PID注意甄别。3.5 第五步确认 Arthas 自身依赖是否完整最后一步如果前面所有操作都正常但 Arthas 还是报错那就得怀疑 Arthas 这个 jar 本身是否健康了。Arthas 在启动过程中会加载自身依赖的类库尤其是 fastjson。如果类路径里存在老版本或冲突版本的 fastjson可能造成启动过程异常中断表现看起来就像是没找到进程。常见的症状是启动日志里出现Exception in thread main com.alibaba.fastjson.JSONException或者干脆在打印进程列表之前就抛异常。遇到这类情况建议去 Arthas 的 GitHub Releases 页重新下载最新版arthas-boot.jar官方地址是https://github.com/alibaba/arthas/releases下载后对比一下 SHA-256 再使用。你也可以用另一种方式安装 Arthas 到本地如果你用 HomebrewmacOS / Linux 环境可以执行brew install arthasbrew 会帮您管理好版本、依赖和启动脚本省去手动下载 jar 的麻烦。补充一点Arthas 需要 JDK 8 及以上版本才能运行。如果你机器上默认的java是 JRE没有tools.jar和 attach 相关的类也会导致无法 attach。这一步很关键执行java -version确认版本执行which javac确认是 JDK 而非纯 JRE。纯 JRE 环境下 Arthas 是没法工作的。4. 真实场景专项分析不同环境下的表现和处理方式同一个问题在不同环境里的长相差异非常大。这一节我挑四个典型场景把每个场景下的关键特征和应对方法单独拎出来讲。4.1 场景一本地 IDEA 启动的应用Arthas 找不到进程很多人在本地开发时也会用 Arthas终端里执行java -jar arthas-boot.jar结果发现列表里空空的自己的 Spring Boot 应用没有被列出来。原因通常不是权限而是 IDEA 的 Launcher 机制。IDEA 启动应用时默认使用com.intellij.rt.execution.application.AppMainV2来代理执行 main 方法进程名虽然是 java但主类和启动参数不太一样Arthas 在某些版本下会识别不出来。解决办法很简单给 IDEA 的 JVM 启动参数里加上-Djava.rmi.server.hostname127.0.0.1之类的参数通常没用真正有效的是在 IDEA 中配置 JVM 参数时显式添加-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port1099 -Dcom.sun.management.jmxremote.authenticatefalse -Dcom.sun.management.jmxremote.sslfalse等等这个配置是让 JMX 可被外部工具连接的并不直接影响 Arthas 的 attach。实际上 Arthas 查找进程走的是VirtualMachine.list()不依赖 JMX。IDEA 场景下更靠谱的做法是用jps -lv找到 PID然后直接执行java -jar arthas-boot.jar PID来指定。实操心得我在本地调试时已经形成固定流程——先用jps找到 PID再直接用java -jar ~/arthas/arthas-boot.jar PID去 attach。这样最省事不用等 Arthas 慢慢出列表。4.2 场景二Linux 服务器上通过 jar 包部署的服务生产环境最常见的部署方式就是nohup java -jar myapp.jar app.log 21 。这种情况下进程的启动用户通常是你登录的用户理论上不会出现权限问题。但如果你用systemd管理服务配置文件里指定了Userxxx那进程就是以 xxx 用户运行你得切到 xxx 用户再执行 Arthas。我在多个项目里都见过调度脚本里写明用su -s /bin/bash nobody -c java -jar /opt/app.jar来启动进程的。这种场景下进程属于nobody你拿自己的账号执行 Arthas同样找不到。解决方式还是那句老话sudo -u nobody java -jar arthas-boot.jar。也可以通过jps -lv来查看每个启动参数里的关键信息比如jps -lv | grep app.jar这样能确认 PID、确认启动用户还能看到-Dspring.profiles.active这类环境配置。如果是服务部署在/opt/下可能存在 SELinux 限制导致 attach 被拒绝。上述命令里如果不确定可以附加-q查 PID再用ps -o pid,user,cmd -p PID查归属用户和完整命令行。注意这类服务器上多半有多个 Java 应用直接执行 Arthas 会列出全部进程这时候别选错 PID选错了 attach 到别人的服务上虽然一般不会造成破坏但操作目标是错的排查结果毫无意义。4.3 场景三Docker 容器里的 Java 服务最容易踩坑现在微服务上容器是主流Docker 容器里的 Java 进程让很多人栽过跟头。如果你在宿主机上直接执行java -jar arthas-boot.jar大概率是找不到容器里的 Java 进程的。原因有两个层面第一容器有自己独立的 PID namespace。宿主机上的 PID 和容器内的 PID 不是一回事你在宿主机上看到的进程号跟容器内ps看到的进程号可能完全不同。Arthas 的 attach 机制依赖的是宿主机级别的/proc和/tmp/hsperfdata目录而容器内的路径对宿主机来说可能不可见。第二hsperfdata 文件的权限单独在容器内部管理。容器内启动 Java 进程的用户比如java用户UID 1000和宿主机上跑 Arthas 的用户不一致即使能读/proc也读不了/tmp/hsperfdata_java。正确做法是进入容器内部执行 Arthasdocker exec -it container_id /bin/bash java -jar arthas-boot.jar前提是容器里装了 JDK而且arthas-boot.jar在容器里能访问到。如果容器很精简连jps都没有那还是先把 JDK 装进去或者复制一个静态编译的 JDK 进去。避坑技巧有些容器是FROM openjdk:8-jre-alpine这类精简镜像里面连/tmp/hsperfdata都没有或者被只读挂载了。遇到这种情况我一般直接在 Dockerfile 里显式创建目录并授权RUN mkdir -p /tmp/hsperfdata_java chmod 777 /tmp/hsperfdata_java或者在启动命令中动态加-XX:-UsePerfData这个参数这个参数会关闭 perf data 的生成但问题也会随之消失没有了 perf data 文件attach 时连路径都找不到。总之生产环境中如果需要 Arthas 排查足够重视容器镜像的 perfdata 环境是很重要的。关于 K8s 环境多说一句如果你在 K8s 里跑应用首选方式是kubectl exec -it pod -- java -jar arthas-boot.jar直接进 Pod 里操作。如果是 InitContainer 或 sidecar 模式共享进程命名空间需要注意 PID 映射。其实在 K8s 集群中更合理的做法是给目标项目集成 Arthas Tunnel Server 或使用 Kubernetes 插件但这个主题比较大这里先不展开。大家记住最核心的一条Arthas 要和目标 JVM 在同一个可通信的进程命名空间里且用户权限匹配。4.4 场景四多 JVM 实例和自定义进程名的特殊情况如果你机器上有多个 Java 应用或者其他中间件如 Kafka、ZooKeeper也以 Java 进程运行那么 Arthas 的进程列表会非常长识别起来非常烦。jps的优势在这里就体现出来了它不会列出命令行里不含java的进程严格来说jps 本身也只枚举 JVM 实例不含资源占用高的非 JVM 进程。但如果你用ps | grep java去筛可能会把 CI 工具的守护进程比如 Jenkins 是个 Java 应用、监控 agent 也一起筛出来。还有一种特殊情况有些团队为了隐藏进程信息或者做安全加固会把java二进制改名比如叫jre8、myapp-runtime。这种情况下ps | grep java根本找不到Arthas 自动检测自然失效。解决办法是先通过ps -ef找到目标进程的真实 PID然后用ls -l /proc/PID/exe看一下实际二进制路径确认它确定是 Java 进程后再通过java -jar arthas-boot.jar PID强制 attach。ls -l /proc/23456/exe输出类似.../jre8/bin/java说明它确实是改名后的 JVM。传 PID 参数的方案是最稳的。5. 常见问题与排查技巧速查表按上面那套五步法绝大多数问题都能解决。但如果还是卡住下面这张速查表可以作为补充结合错误信息和环境特征快速定位根因。错误信息可能原因解决建议Can not find java process. Try to pass pid in command line.无法枚举到符合条件的 JVM 实例确认jps和jps -lv输出按五步法逐层排查process information unavailablehsperfdata 目录无法访问或当前用户不是目标进程启动用户切换用户重试修复/tmp/hsperfdata_*权限Unable to open socket fileattach 所需的 socket 文件缺失或不可读检查/tmp/hsperfdata_*/目录权限避免在不同用户间切换必要时重启应用恢复AttachNotSupportedException目标 JVM 不是标准 HotSpot JVM或 Java 版本低于 8确认 JDK 类型与版本替换为兼容的 JVMAddress already in useArthas listen 端口被占用换端口启动java -jar arthas-boot.jar --telnet-port 9999 --http-port 8563fastjson.JSONExceptionArthas 依赖的 fastjson 类冲突升级 Arthas 版本或切换安装方式如 brew/官方脚本另外再补充几个排查思路如果你用top或htop看到 CPU 占用异常进程但jps里没有它先判断它是否真的是 JVM 进程。可以用/proc/PID/status看进程名和命令行排除自定义二进制或改名 JVM。如果应用部署在 Kubernetes 中并且有多个副本注意 Pod 被调度到不同节点的问题。你要排查哪个 Pod就要进到对应的节点或直接用kubectl exec进入 Pod别在错误的机器上白忙活。遇到某台服务器上所有 Java 进程都 attach 不了的情况优先怀疑/tmp目录或 hsperfdata 目录下的文件被清理。遇到这种非常规情况最干净的办法是重启受影响的应用让 perf data 重新生成。如果线上不方便用jps可以下载 jdk 的 bin 目录单独复制 jps 和 lib 进来或者用cat /proc/stat查看进程信息但更推荐直接在你的 CI/CD 流程里预留一个便携 JDK用官方基础镜像的 jdk 版本能省很多事。6. 最后的个人经验总结Arthas 启动找不到进程十次里有八次是权限或用户身份问题剩下两次是环境隔离容器、改名进程、目录清理造成的。这套问题并不是什么高深莫测的技术难题但确实需要你在多个层面依次排查按顺序排除才能最快定位。我个人在实际操作中的习惯是拿到一台新服务器先执行jps -lv和ps -ef | grep java两个命令的结果放一起对比既能确认进程是否存在又能顺便确认启动用户和 JVM 参数。然后再动手执行 Arthas。这三步走完90% 的问题就已经有答案了。剩下 10% 如果还不行再回头看 hsperfdata 目录和 Arthas 依赖基本没有解不掉的。最后再分享一个小技巧Arthas 启动时如果你有时间可以把arthas-boot.jar放到固定的目录比如~/tools/arthas/同时配置一个 shell aliasalias arthasjava -jar ~/tools/arthas/arthas-boot.jar这样每天排查问题时敲命令都少几个键省下来的时间虽然不多但关键时刻那几秒可能就是服务恢复的黄金时间。