如何获取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和更高版本中都包含jstackjps

我推荐使用JDK(jvisualvm.exe)分发的Java VisualVM。 它可以dynamic连接并访问线程和堆。 我发现在一些问题上是无价的。

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

比较JVM选项参考页面

如果你在server-jre 8以上,你可以使用这个:

 jcmd PID GC.heap_dump /tmp/dump 

尝试以下选项之一。

  1. 对于32位JVM:

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

     jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid> 
  3. 对于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 javanetstat -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(使用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% 

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

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

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

Interesting Posts