linux下jcmd没法获取jvmdump

现象:

前两天在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]
---------------------------

原理解析:

jcmd原理

  • 当使用此命令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.

参考说明:

  1. privateTmp介绍 http://www.javashuo.com/article/p-tavmjlxa-nw.html
  2. https://lists.centos.org/pipermail/centos/2015-April/151589.html
  3. http://0pointer.de/blog/projects/security.html
  4. https://access.redhat.com/blogs/766093/posts/1976243
相关文章
相关标签/搜索