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

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

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


当前回答

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

其他回答

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

下面的脚本使用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脚本。

您可以使用jmap获取正在运行的任何进程的转储,假设您知道pid。

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

jmap -dump:format=b,file=heap.hprof <pid>

获取该进程的堆。

对于安装了bash和pgrep并且正在运行单个Java进程的系统,请尝试:

jmap -dump:format=b,file=heap.hprof $(pgrep java)

如果你在服务器-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及更高版本中。