現象:
前兩天在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.
參考說明: