如何获取Windows上未运行在控制台上的Java进程的线程和堆转储
我有一个Java应用程序,我从一个控制台运行,这又是另一个Java进程。 我想获得该subprocess的线程/堆转储。 在Unix上,我可以做一个“杀-3”,但在Windows AFAIK获得线程转储的唯一方法是控制台中的Ctrl-Break。 但这只能给我父母的过程,而不是孩子的转储。 有没有另一种方法来获得堆转储?
假设你知道pid
,你可以使用jmap
来获得任何正在运行的进程的转储。
使用任务pipe理器或资源监视器来获取pid
。 然后
jmap -dump:format=b,file=cheap.bin <pid>
为这个过程获得堆。
你混淆了两个不同的java转储。 kill -3
生成一个线程转储,而不是堆转储。 线程转储= JVM输出中每个线程的堆栈跟踪作为文本标准输出。 堆转储= JVM进程输出到二进制文件的内存内容。
要在Windows上进行线程转储,如果您的JVM是前台进程, 则按CTRL + BREAK是最简单的方法。 如果你像Cygwin或MobaXterm那样在Windows上有一个像unix一样的shell,你可以像在Unix中一样使用kill -3 {pid}
。
要在Unix中进行线程转储,如果您的JVM是前台进程,请使用CTRL + C,否则只要您为JVM获得正确的PID, kill -3 {pid}
就会工作。
在任何一个平台上,Java都带有一些可以提供帮助的工具。 对于线程转储, jstack {pid}
是你最好的select。 http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html
只是为了完成转储问题:堆转储不常用,因为它们很难解释。 但是,如果您知道在哪里/如何看待这些信息,那么他们中就有很多有用的信息。 最常见的用法是定位内存泄漏。 在java命令行上设置-D
是一个很好的做法,以便在OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError
自动生-XX:+HeapDumpOnOutOfMemoryError
转储。但是,您也可以手动触发堆转储。 最常见的方法是使用Java实用程序jmap
。
注意:此实用程序不适用于所有平台。 从JDK 1.6开始, jmap
在Windows上可用。
示例命令行看起来像jmap -dump:file=myheap.bin {pid of the JVM}
。 输出“myheap.bin”不是人类可读的(对于我们大多数人),您将需要一个工具来分析它。 我的首选是MAT。 http://www.eclipse.org/mat/
我认为在Linux进程中创build.hprof文件的最好方法是使用jmap命令。 例如: jmap -dump:format=b,file=filename.hprof {PID}
除了使用提到的jconsole / visualvm之外,您可以在另一个命令行窗口上使用jstack -l <vm-id>
,并捕获该输出。
可以使用任务pipe理器(它是windows和unix上的进程ID)或者使用jps
来find<vm-id>。
Sun JDK版本6和更高版本中都包含jstack
和jps
。
我推荐使用JDK(jvisualvm.exe)分发的Java VisualVM。 它可以dynamic连接并访问线程和堆。 我发现在一些问题上是无价的。
如果你想在内存不足的情况下使用heapdump,可以使用选项-XX:-HeapDumpOnOutOfMemoryError
启动Java -XX:-HeapDumpOnOutOfMemoryError
比较JVM选项参考页面
如果你在server-jre 8以上,你可以使用这个:
jcmd PID GC.heap_dump /tmp/dump
尝试以下选项之一。
-
对于32位JVM:
jmap -dump:format=b,file=<heap_dump_filename> <pid>
-
对于64位JVM(明确引用):
jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
-
对于VM参数中的G1GCalgorithm的64位JVM(仅使用G1GCalgorithm生成活动对象堆):
jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
相关的SE问题: 使用jmap命令的Java堆转储错误:过早的EOF
看看这篇文章中的各种jmap
选项
您可以运行jconsole
(包含在Java 6的SDK中),然后连接到您的Java应用程序。 它会显示你每一个线程运行和它的堆栈跟踪。
你可以从Cygwin发送kill -3 <pid>
。 您必须使用Cygwin ps
选项来查找窗口进程,然后将信号发送到该进程。
你必须将第二个java可执行文件的输出redirect到某个文件。 然后,使用SendSignal发送“-3”到您的第二个进程。
如果您使用的是JDK 1.6或更高版本,则可以使用jmap
命令来进行Java进程的堆转储,条件是您应该知道ProcessID。
如果你在Windows机器上,你可以使用任务pipe理器来获得PID。 对于Linux机器,您可以使用各种命令,如ps -A | grep java
ps -A | grep java
或netstat -tupln | grep java
netstat -tupln | grep java
或者top | grep java
top | grep java
,取决于你的应用程序。
然后你可以使用像jmap -dump:format=b,file=sample_heap_dump.hprof 1234
这样的命令jmap -dump:format=b,file=sample_heap_dump.hprof 1234
其中1234是PID。
有多种工具可用来解释hprof文件。 我会推荐使用Oracle的visualvm工具,这个工具很简单。
我为Java 8(使用PsExec
和jcmd
)编写了一个名为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%
它必须在启动JVM的用户的同一Windows会话中运行,因此如果通过远程桌面连接,则可能需要在Session 0
启动命令提示符并从此处运行它。 例如
%PsExec% -s -h -d -i 0 cmd.exe
这会提示你(单击底部的任务栏图标)在交互式会话中View the message
,这会将您带到另一个会话中的新控制台,从中可以运行jvmdump.bat
脚本。