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

在Unix上,我可以执行kill -3 <pid>,但在Windows AFAIK上,获得线程转储的唯一方法是在控制台中按Ctrl-Break。但这只给了我父进程的转储,而不是子进程的转储。

有其他方法来获取堆转储吗?


当前回答

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

我为Java 8编写了一个名为jvmdump.bat的小批处理脚本(使用PsExec和jcmd),它可以转储线程、堆、系统属性和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%

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

%PsExec% -s -h -d -i 0 cmd.exe

这将提示您(单击底部的任务栏图标)查看交互式会话中的消息,这将把您带到另一个会话中的新控制台,从中可以运行jvmdump.bat脚本。

其他回答

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

试试下面的一种选择。

对于32位JVM: Jmap -dump:format=b,file=<heap_dump_filename> <pid> 64位JVM(显式引用): jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid> 对于64位JVM,在VM参数中使用G1GC算法(仅使用G1GC算法生成活对象堆): jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>

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

看看本文中jmap的各种选项

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

jcmd PID GC.heap_dump /tmp/dump

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

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

jstack和jps都包含在Sun JDK版本6及更高版本中。

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

执行“jcmd”命令可获取java应用程序的进程id。

如何获得线程转储?

jcmd PID线程。打印>线程

参考链接

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

如何获得堆转储?

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

PID:应用程序的进程号。参考链接