现象:
前两天在linux上的服务出现莫名其妙的内存溢出.却发现没法用jcmd链接jvm获取dump.现象:html
[root@host-12.131.14.15 bin]# ./jcmd 19652 GC.heap_dump
19652:
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:208)
at sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:147)
at sun.tools.jcmd.JCmd.main(JCmd.java:131)
解决方案:
若是启动用户不是同一个,切换成同一用户.
使用命令以下:
sudo -u [userid]
/jcmd 19652 GC.heap_dump
若是启动用户已是同一个还报错,则去
/usr/lib/systemd/system/ 服务位置 ,观察服务对应的PrivateTmp属性是否为true.具体以下:
-----------------------
[unit]
description=xxx
[Service]
Type=forking
ExecStartPre=/
ExecStart=
ExecStop=
PrivateTmp=true
[Install]
WantedBy=multi-user.target
-------------------------
若PrivateTmp为true,改成false,并使用以下命令刷新服务便可.
--------------------------
systemctl daemon-reload
systemctl restart [servicename]
systemctl status
[servicename]
---------------------------
原理解析:
- 当使用此命令dump内存时.在链接对应java进程的pid以前,将会jcmd会生成一个.attach_pid在目标程序的工做目录或者/tmp.
- 而后jcmd发送SIGQUIT到目标进程.当虚拟机获取到这个信号而且发现了.attache_pid,将会开启一个AttachListener 进程.
- AttachListener 进程使用UNIX 的socket/tmp/.java_pid去和jcmd工具打交道
- 考虑到安全缘由,当一个链接(从jcmd发出的)被接收后,虚拟机会检查socket链接的建立的用户是否和jvm进程的euid或egid一致.这是为jcmd在不一样用户的状况不工做的缘由.(root的状况也不可以使用)
- jcmd链接上socket后,将会收到dumpheap.
privateTmpjava
- 本问题排查时,其实用户已是同一个用户,可是获取不到,是由于服务的privateTmp机制.当service unit中的privateTmp设置为true时,service会将$tmp_file放在linux的tmp/systemd-private-xxxxx-[servicename].service/xxx中.
-
privateTmp用于设置是否使用私有的tmp目录,那么只要设置使用这个属性的service,都会使用私有的tmp目录。 好比说: nginx会有一个systemd-private-xxx-nginx.service/tmp目录
-
默认的/tmp目录通常全部用户的全部service共享的,对于全部用户及用户运行的程序来讲来讲,都会有读和写的权限.会存在一些安全性问题.把各个service的tmp目录隔离开的话,能够保证必定的安全性.
-
对于这个jcmd没法heapdump的问题,在能确认服务器安全的状况下,彻底能够考虑关闭掉该配置项.固然,若是服务器安全得不到保障的状况下,或者应用在跑不能重启的状况下,能够经过更改service unit中的execStart execStop对应中的脚原本解决.
例如,咱们服务的命令以下:
-----------------------
[unit]
description=xxx
[Service]
Type=forking
ExecStartPre=/
ExecStart=/opt/app/start.sh
ExecStop=
/opt/app/stop.sh
PrivateTmp=true
[Install]
WantedBy=multi-user.target
-------------------------
能够经过修改/opt/app/stop.sh脚本,经过stop.sh中的
./jcmd 19652 GC.heap_dump /opt/xxx/servicedump.hprof
来获取dump.
参考说明: