解决 JVM AttachNotSupportedException 的问题

对于 JVM GC overhead 的问题, 通常要在 overhead 很高的时候做 heap dump, 这样捕获的 dump 文件才更有意义. 可是在 GC overhead 很高的时候, 它消耗的 CPU 也很高, 通常把机器的 CPU 全占满. 这个时候尝试去使用 jcmd, jmap 去做 heap dump, 很高的概率会得到这个异常:

com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded
    at sun.tools.attach.LinuxVirtualMachine.<init>(LinuxVirtualMachine.java:106)
    at sun.tools.attach.LinuxAttachProvider.attachVirtualMachine(LinuxAttachProvider.java:63)
    at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:213)
    at sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:140)
    at sun.tools.jcmd.JCmd.main(JCmd.java:129)

那么如何解决这个问题呢?

假的技术问题:

有些时候, 你看到这个问题, 并不一定是因为 CPU 太忙, 该进程不响应造成的. 在使用 jcmd/jmap 之前, 看看启动目标进程的用户是谁? 通常我们都是以一个专门的账号启动我们的应用. 使用 jcmd/jmap/jstack 等的前提条件是使用同一个账号, 这样进程间才能方便的进行 socket 通信.

有没有解决方案:

假如我的目标应用和当前命令已经使用同一个账号了, 还出现这个问题, 那是什么原因呢?
那是因为, 当目标进程的所有 GC 线程都在忙碌的时候, 它基本是百分比占用 CPU 的, 一旦有时间片 (time slice)分配给目标 JVM 进程, 它就完全使用它做 GC. 通常这个时候的堆基本占满, 要做非常繁重的连续 CPU GC 运算. 所以目标进程很难抽出时间去响应 socket 请求.
所以, 目前只有一个可行的, 但是不是很好的方案: 那就是不断的连续的去使用命令尝试 socket 连接, 只要有机会目标线程恰好抽出时间响应. 这个响应的时间很有可能是在 2 个 full GC 的中间某个时间点. 这样的时机点比较少, 因为大部分时间目标进程都在做 Full GC 的运算.

一旦找到这个时间点, 开始响应你的请求, 就可以看到原来的 full GC 就停下了, 这时候开始响应 socket 了.

更新 2020-03-5
另外一种方案是: 先产生系统 core dump, 然后再从 core dump 转成 heap dump.
关于如何产生 core dump: https://linux-audit.com/understand-and-configure-core-dumps-work-on-linux/
如何转 core dump 为 heap dump:
转换 core dump 为 HPROF 文件
_$jmap -dump:format=b,file=dump.hprof /usr/bin/java core.1234 //这个 java 是你 core dump 运行的 java

gcovhead_dump.png

标签: none

添加新评论