如何在Windows上获取未在控制台中运行的Java进程的线程和堆转储

227 java jvm thread-dump heap-dump

我有一个Java应用程序,我从控制台运行,然后控制台执行另一个Java进程.我想获得该子进程的线程/堆转储.

在Unix上,我可以做一个kill -3 <pid>但是在Windows AFAIK上获取线程转储的唯一方法是在控制台中使用Ctrl-Break.但这只会让我转移父进程,而不是孩子.

有没有另一种方法来获得堆转储?

小智 370

jmap假设你知道,你可以用来获取正在运行的任何进程的转储pid.

使用任务管理器或资源监视器来获取pid.然后

jmap -dump:format=b,file=cheap.hprof <pid>
Run Code Online (Sandbox Code Playgroud)

获取该进程的堆.

  • 这个线程变得如此受欢迎,以至于我刚刚听到有人将堆转储称为"cheap.bin" (165认同)
  • 一个更直接的文件名:"heap.hprof",因为它是HPROF格式. (5认同)
  • 确保使用启动 java 进程的正确用户。就我而言,它是 tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump:format=b,file=&lt;filename&gt; &lt;pid&gt; (2认同)

Der*_*rek 114

您混淆了两个不同的Java转储. kill -3生成线程转储,而不是堆转储.

线程转储=将JVM输出中的每个线程的堆栈跟踪作为文本输出到stdout.

堆转储= JVM进程输出到二进制文件的内存内容.

要在Windows上进行线程转储,CTRL+ BREAK如果您的JVM是前台进程是最简单的方法.如果你在Windows上有类似unix的shell,比如Cygwin或MobaXterm,kill -3 {pid}你可以在Unix中使用.

要在Unix中进行线程转储,CTRL+ C如果您的JVM是前台进程,或者kill -3 {pid}只要您为JVM获得正确的PID就可以工作.

无论使用哪种平台,Java都有几个可以提供帮助的实用程序.对于线程转储,jstack {pid}是你最好的选择.http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

只是为了完成转储问题:堆转储不常用,因为它们很难解释.但是,如果你知道在哪里/如何看待它们,它们中有很多有用的信息.最常见的用法是查找内存泄漏.最好-D在java命令行上设置,以便在OutOfMemoryError上自动生成堆转储,-XX:+HeapDumpOnOutOfMemoryError 但是,您也可以手动触发堆转储.最常见的方法是使用java实用程序jmap.

注意:此实用程序并非在所有平台上都可用.从JDK 1.6开始,jmap可在Windows上使用.

示例命令行看起来像

jmap -dump:file=myheap.bin {pid of the JVM}
Run Code Online (Sandbox Code Playgroud)

输出"myheap.bin"不是人类可读的(对于我们大多数人来说),你需要一个工具来分析它.我的偏好是MAT. http://www.eclipse.org/mat/

  • 在我的linux上Ctrl-C中断(终止)它,我做Ctrl- \ (3认同)

小智 30

我认为在Linux进程中创建.hprof文件的最佳方法是使用jmap命令.例如:jmap -dump:format=b,file=filename.hprof {PID}


ank*_*kon 19

除了使用上面提到的jconsole/visualvm之外,您还可以jstack -l <vm-id>在另一个命令行窗口中使用,并捕获该输出.

可以使用任务管理器(它是windows和unix上的进程ID)或使用来找到<vm-id> jps.

两者jstackjps有包括6和更高太阳的JDK版本.

  • 您可能正在混淆JDK和JRE,我明确提到了JDK.请参阅工具的文档:http://download.oracle.com/javase/6/docs/technotes/tools/share/jstack.html和http://download.oracle.com/javase/6/docs/technotes /tools/share/jps.html (7认同)

Law*_*Dol 16

我推荐使用JDK(jvisualvm.exe)分发的Java VisualVM.它可以动态连接并访问线程和堆.我发现一些问题非常宝贵.

  • @Jaberino:不,这是关于当前正在运行的Java进程,在Windows中,没有与之关联的控制台. (3认同)
  • 这大部分时间都不可行,因为它有附加的开销,并且通常从生产机器检索线程转储. (2认同)

Rav*_*abu 15

请尝试以下选项之一.

  1. 对于32位JVM:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 对于64位JVM(明确引用):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
    
    Run Code Online (Sandbox Code Playgroud)
  3. 对于VM参数中具有G1GC算法的64位JVM(仅使用G1GC算法生成活动对象堆):

    jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
    
    Run Code Online (Sandbox Code Playgroud)

相关的SE问题:使用jmap命令的Java堆转储错误:过早的EOF

看看各种选项jmap,在此文章


Atu*_*man 14

如果你在服务器jre 8及以上,你可以使用这个:

jcmd PID GC.heap_dump /tmp/dump
Run Code Online (Sandbox Code Playgroud)

  • 在大多数生产系统中,我们只有jre,没有jdk。所以这有帮助。 (2认同)

Dan*_*ein 12

如果要在内存不足的情况下使用heapdump,可以使用该选项启动Java -XX:-HeapDumpOnOutOfMemoryError

cf JVM选项参考页面


Yon*_*oit 7

您必须将输出从第二个 java 可执行文件重定向到某个文件。然后,使用SendSignal发送“-3”你的第二个过程。


kro*_*old 6

你可以kill -3 <pid>从Cygwin 发送.您必须使用Cygwin ps选项来查找Windows进程,然后将信号发送到该进程.


Ste*_*Kuo 6

您可以运行jconsole(包含在Java 6的SDK中),然后连接到您的Java应用程序.它将向您显示每个Thread运行及其堆栈跟踪.


isa*_*pir 5

以下脚本使用 PsExec 连接到另一个 Windows 会话,因此即使通过远程桌面服务连接也能正常工作。

我为 Java 8(使用PsExecjcmd)编写了一个名为 的小批量脚本jvmdump.bat,它转储线程、堆、系统属性和 JVM 参数。

:: set the paths for your environment
set PsExec=C:\Apps\SysInternals\PsExec.exe
set JAVA_HOME=C:\Apps\Java\jdk1.8.0_121
set DUMP_DIR=C:\temp

@echo off

set PID=%1

if "%PID%"=="" (
    echo usage: jvmdump.bat {pid}
    exit /b
)

for /f "tokens=2,3,4 delims=/ " %%f in ('date /t') do set timestamp_d=%%h%%g%%f
for /f "tokens=1,2 delims=: " %%f in ('time /t') do set timestamp_t=%%f%%g
set timestamp=%timestamp_d%%timestamp_t%
echo datetime is: %timestamp%

echo ### Version >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.version >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Uptime >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.uptime >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Command >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.command_line >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Flags >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.flags >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Properties >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.system_properties >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% Thread.print -l >"%DUMP_DIR%\%PID%-%timestamp%-threads.log"

%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% GC.heap_dump "%DUMP_DIR%\%PID%-%timestamp%-heap.hprof"

echo Dumped to %DUMP_DIR%
Run Code Online (Sandbox Code Playgroud)

它必须在启动 JVM 的用户的同一个 Windows 会话中运行,因此如果您通过远程桌面连接,您可能需要在其中启动命令提示符Session 0并从那里运行它。例如

%PsExec% -s -h -d -i 0 cmd.exe
Run Code Online (Sandbox Code Playgroud)

这将提示您(单击底部的任务栏图标)进入View the message交互式会话,这将带您进入另一个会话中的新控制台,您可以从中运行jvmdump.bat脚本。


Har*_*hna 5

如何获取java应用程序的进程ID?

执行命令 'jcmd' 以获取 java 应用程序的进程 ID。

如何获得线程转储?

jcmd PID Thread.print > thread.dump

参考 链接

您甚至可以使用 jstack 来获取线程转储(jstack PID > thread.dump)。参考链接

如何获得堆转储?

使用 jmap 工具获取堆转储。jmap -F -dump:live,format=b,file=heap.bin PID

PID 代表应用程序的进程 ID。参考链接