zoukankan      html  css  js  c++  java
  • linux下jcmd无法获取jvmdump

    现象:

    前两天在linux上的服务出现莫名其妙的内存溢出.却发现无法用jcmd连接jvm获取dump.现象:

    [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.

    privateTmp

    • 本问题排查时,其实用户已经是同一个用户,但是获取不到,是因为服务的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介绍 https://www.cnblogs.com/lihuobao/p/5624071.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
  • 相关阅读:
    [nginx]简单内容管理系统的spring项目的nginx.conf配置文件的注释理解
    [日常填坑]部署使用Idea开发的spring框架的多模块项目到服务器
    [日常填坑]centos7.0+版本服务器安装jdk9或者jdk10
    [日常填坑]centos7.0+版本服务器安装mysql
    机器学习实战之kNN算法
    机器学习实战之机器学习主要任务
    模式字符串匹配问题(KMP算法)
    ubuntu 更新引导命令
    ubuntu 14.04 中找不到 libgtk-x11-2.0.so
    maven project 更新总是jre-1.5
  • 原文地址:https://www.cnblogs.com/alcc/p/9905705.html
Copyright © 2011-2022 走看看