Jmap无法连接以进行转储。

62
我们有一个正在进行公开测试的应用程序,偶尔会导致堆空间溢出。JVM通过永久性休眠来回应。为了分析这个问题,我想在失败的那一点上查看内存。但Java不允许我这样做。该进程仍在内存中,但似乎没有被识别为Java进程。涉及的服务器是Debian Lenny服务器,Java 6u14版本。
/opt/jdk/bin# ./jmap -F -dump:format=b,file=/tmp/apidump.hprof 11175
Attaching to process ID 11175, please wait...
sun.jvm.hotspot.debugger.NoSuchSymbolException: Could not find symbol "gHotSpotVMTypeEntryTypeNameOffset" in any of the known library names (libjvm.so, libjvm_g.so, gamma_g)
at sun.jvm.hotspot.HotSpotTypeDataBase.lookupInProcess(HotSpotTypeDataBase.java:390)
at sun.jvm.hotspot.HotSpotTypeDataBase.getLongValueFromProcess(HotSpotTypeDataBase.java:371)
at sun.jvm.hotspot.HotSpotTypeDataBase.readVMTypes(HotSpotTypeDataBase.java:102)
at sun.jvm.hotspot.HotSpotTypeDataBase.<init>(HotSpotTypeDataBase.java:85)
at sun.jvm.hotspot.bugspot.BugSpotAgent.setupVM(BugSpotAgent.java:568)
at sun.jvm.hotspot.bugspot.BugSpotAgent.go(BugSpotAgent.java:494)
at sun.jvm.hotspot.bugspot.BugSpotAgent.attach(BugSpotAgent.java:332)
at sun.jvm.hotspot.tools.Tool.start(Tool.java:163)
at sun.jvm.hotspot.tools.HeapDumper.main(HeapDumper.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at sun.tools.jmap.JMap.runTool(JMap.java:179)
at sun.tools.jmap.JMap.main(JMap.java:110)
Debugger attached successfully.
sun.jvm.hotspot.tools.HeapDumper requires a java VM process/core!

3
您说的是 jmap 是从您安装的 Hotspot JVM 中执行的。您确定应用程序本身是使用相同的 Java 版本启动的吗? - Eyal Schneider
我再次检查以确保,但机器上只有一个Java版本,并且我直接从bin目录调用jmap。没有PATH错误。因此,它绝对是相同的JVM。这里有人提出的一个可能性是与-XX:+ UseCompressedOops参数有关。 - Jasper Floor
我遇到了这样的错误:"附加到进程时出错:sun.jvm.hotspot.debugger.DebuggerException: 无法附加到进程。" - Vijay Kumar
2
在Linux下,您可能需要使用“sudo…”来获得对进程的适当访问权限。 - Thorbjørn Ravn Andersen
14个回答

98

解决方案非常简单。我以root身份运行了jmap,但我必须以启动jvm的用户身份运行它。现在,我会羞愧地躲起来。


13
OR,Sun可以使错误信息更加易懂,而不是仅写“需要一个Java虚拟机进程/核心!” - codeObserver
4
你用什么命令来解决这个问题的?我正在尝试使用 su -m myuser -c 'jmap -J-d64 -dump:format=b,file=/tmp/apidump.hprof 98233',但是我得到的回应是“Operation not permitted”。请问有什么建议吗? - ocramot
./jmap -F -dump:format=b,file=/tmp/apidump.hprof 11175但这都是很久以前的事了。 - Jasper Floor
1
九年后,仍在帮助人们。 :) - David
Open JDK 14 jhsdb jmap --heap --pid 1 Open JDK 14 jhsdb jmap --heap --pid 1 - Alex Punnen

39

我使用相同的用户运行jmap和应用程序,仍然遇到错误。

解决方案是在运行jmap之前运行该命令。

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

那就使用jmap,它可以正常工作。

jmap -heap 17210

2
在这里记录了:http://www.thecodingmachine.com/fixing-java-memory-leaks-in-ubuntu-11-04-using-jmap/:“错误并不明显,但来自于Ubuntu中ptrace的配置。” 这个命令重新配置。作为JDK-7050524记录。 - Andy MacKinlay
Andy提供的文档链接已经失效。这个链接可能会有所帮助:https://askubuntu.com/questions/146160/what-is-the-ptrace-scope-workaround-for-wine-programs-and-are-there-any-risks - Champ
这也发生在我的Ubuntu 18和OpenJDK 11.0.2上。jhsdb jmap --pid 8236 正在附加到进程ID 8236,请稍候... 附加到进程时出错:sun.jvm.hotspot.debugger.DebuggerException: 无法附加到进程:对于8236,ptrace(PTRACE_ATTACH,..)失败:操作不允许。 - shapiy

20

你的链接似乎已经失效了。请问你能否分享正确的链接? - A.K.Desai
链接现在可以使用了,在@A.K.Desai的评论后更新了文本。 - Per Lundberg
3
当在Docker中运行的java进程以非root用户身份运行时,这对我没有用。当以root身份运行时,我会得到无法打开套接字文件:目标进程未响应或HotSpot VM未加载的错误,而以非root用户身份运行时,我会得到权限被拒绝的错误。 - Per Lundberg

16

未来的谷歌员工们:

如果你在试图对正在运行的进程使用 jmap 命令时安装了 JDK,也可能发生这种情况。

如果是这种情况,请重新启动 Java 进程。


2
如果您无法重新启动Java进程,因为您想在当前状态下调查系统怎么办?这正是我现在的情况:(。我就是这样做的,我在进程运行时安装了JDK。 - Jose Cifuentes

7

请按照以下步骤从 docker 容器中获取线程和堆转储:

  1. 运行以下命令以进入容器的 bash 环境。请适当更改 CONTAINER_NAME。
   docker exec -it CONTAINER_NAME bash
  1. 然后输入jps命令查找所有Java应用程序的详细信息,并提取您的应用程序的PID
jps
  1. Then run the below command to get the thread dump. Please change the PID appropriately

    jstack PID > threadDump.tdump 
    
  2. Then run the below command to get the Heap dump. Please change the PID appropriately

    jmap -dump:live,format=b,file=heapDump.hprof PID 
  1. 然后退出docker容器并通过运行以下命令从docker容器中下载threadDump.tdump和heapDump.hprof文件。请适当更改CONTAINER_NAME。
 sudo docker cp CONTAINER_NAME:threadDump.tdump .
 sudo docker cp CONTAINER_NAME:heapDump.hprof .

  1. 执行 "Docker ps",将给出所有服务的容器 ID,并收集 TSC 的容器 ID。
  2. 执行 "docker exec -it CONTAINER_ID bash"(将 CONTAINER_ID 替换为 TSC 容器 ID)。
  3. Bash 将会启动,然后在 Bash 上执行 "jps",这将为进程提供 PID(对于 jar 文件,它将是 1)。
  4. 执行 "jstack PID > threadDump.tdump"(将 PID 替换为步骤 3 中接收到的进程 ID,应为 1)。
  5. 执行 "jmap -dump:format=b,file=heapDump.hprof PID"(将 PID 替换为步骤 3 中接收到的进程 ID,应为 1)。
- ABHAY JOHRI

6

如果您只运行以下命令会发生什么

./jmap -heap 11175 

你确定应用程序的JVM与JMAP JVM完全相同吗?(版本相同等)

2
在我的情况下:/opt/jvm/jdk1.7.0_15/bin/jmap -heap 2022 连接到进程ID 2022,请稍等... 连接进程时出错:sun.jvm.hotspot.debugger.DebuggerException: 无法连接到该进程。 - Tom Lianza

3
你需要使用随JVM一起提供的jmap工具。

2
1.Execute "Docker ps", will give the container Id of all services and collect the container id foe TSC.
2.Execute "docker exec -it CONTAINER_ID bash" (replace CONTAINER_ID with TSC Container id)
3.Bash will come and then execute the "jps" on bash, that will give you the PID for process(it will be 1 for jar)
4.Execute the "jstack PID > threadDump.tdump"(replace PID with process id received in step 3, it should be 1)
5.Execute the "jmap -dump:format=b,file=heapDump.hprof PID"(replace PID with process id received in step 3, it should be 1)
6.Then we have to exit the bash using "exit" command
7.Execute "sudo docker cp CONTAINER_ID:heapDump.hprof ." from ec2 command line, that will copy the dump file on ec2 machine present working directory.
8.Execute "sudo docker cp CONTAINER_ID:threadDump.tdump ." from ec2 command line, that will copy the dump file on ec2 machine present working directory.

2

我有同样的问题,正在尝试查找在Docker容器内运行的进程中的内存泄漏。我无法使用jmap,而是使用了这个:

jcmd <pid> GC.class_histogram 

这将为您提供内存中对象的列表。而根据Oracle文档:

建议使用最新的工具jcmd,而不是jmap工具,以获得增强的诊断功能和降低性能开销。https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks004.html

2

我在一台安装了两个不同版本的OpenJDK的Linux机器上遇到了相同的jmap错误。首先我安装了OpenJDK 1.6,然后安装了OpenJDK 1.7。

调用...

/usr/lib/jvm/java-1.7.0-openjdk-amd64/bin/java -XshowSettings:properties -version

# produce the following output ...
...
java.library.path = /usr/java/packages/lib/amd64
    /usr/lib/x86_64-linux-gnu/jni
    /lib/x86_64-linux-gnu
    /usr/lib/x86_64-linux-gnu
    /usr/lib/jni
    /lib
    /usr/lib
...
java version "1.7.0_65"

在包含“/usr/lib”时,每个使用OpenJDK 1.7.*启动的程序都会包含第一个安装的JDK(在我的情况下是OpenJDK 1.6.*)的库。因此,Java6和Java7的jmap版本均失败。

在我更改了包含OpenJDK 1.7库的Java7程序的启动后...

/usr/lib/jvm/java-1.7.0-openjdk-amd64/bin/java -Djava.library.path=/usr/lib/jvm/java- \
                  7-openjdk-amd64/jre/lib/amd64/server:/usr/java/packages/lib/amd64: \
                  /usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/ \
                  x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib ...

我能够使用Java 7版本的jmap程序访问进程。但是它需要sudo权限才能运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接