我有一个从控制台运行的Java应用程序,该应用程序反过来执行另一个Java进程。我想获得该子进程的线程/堆转储。
在Unix上,我可以执行kill -3 <pid>,但在Windows AFAIK上,获得线程转储的唯一方法是在控制台中按Ctrl-Break。但这只给了我父进程的转储,而不是子进程的转储。
有其他方法来获取堆转储吗?
我有一个从控制台运行的Java应用程序,该应用程序反过来执行另一个Java进程。我想获得该子进程的线程/堆转储。
在Unix上,我可以执行kill -3 <pid>,但在Windows AFAIK上,获得线程转储的唯一方法是在控制台中按Ctrl-Break。但这只给了我父进程的转储,而不是子进程的转储。
有其他方法来获取堆转储吗?
当前回答
为了从windows中的子java进程中获取线程转储/堆转储,首先需要确定子进程Id。
通过发出命令:jps,您将能够获得在您的windows计算机上运行的所有java进程id。您需要从该列表中选择子进程Id。一旦有了子进程Id,就可以使用各种选项来捕获线程转储和堆转储。
捕获线程转储:
有8个选项来捕获线程转储:
jstack 杀了3 jvisualVM 江铃汽车 Windows (Ctrl + Break) ThreadMXBean APM的工具 jcmd
关于每个选项的详细信息可以在本文中找到。有了捕获线程转储之后,可以使用fastThread、samurai等工具来分析线程转储。
捕获堆转储:
有7个选项可以捕获堆转储:
jmap - xx: + HeapDumpOnOutOfMemoryError jcmd JVisualVM JMX 编程方法 管理控制台
关于每个选项的详细信息可以在本文中找到。捕获堆转储之后,可以使用Eclipse内存分析工具HeapHero等工具来分析捕获的堆转储。
其他回答
下面的脚本使用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脚本。
你可以从Cygwin发送kill -3 <pid>。你必须使用Cygwin ps选项来查找windows进程,然后将信号发送到该进程。
为了从windows中的子java进程中获取线程转储/堆转储,首先需要确定子进程Id。
通过发出命令:jps,您将能够获得在您的windows计算机上运行的所有java进程id。您需要从该列表中选择子进程Id。一旦有了子进程Id,就可以使用各种选项来捕获线程转储和堆转储。
捕获线程转储:
有8个选项来捕获线程转储:
jstack 杀了3 jvisualVM 江铃汽车 Windows (Ctrl + Break) ThreadMXBean APM的工具 jcmd
关于每个选项的详细信息可以在本文中找到。有了捕获线程转储之后,可以使用fastThread、samurai等工具来分析线程转储。
捕获堆转储:
有7个选项可以捕获堆转储:
jmap - xx: + HeapDumpOnOutOfMemoryError jcmd JVisualVM JMX 编程方法 管理控制台
关于每个选项的详细信息可以在本文中找到。捕获堆转储之后,可以使用Eclipse内存分析工具HeapHero等工具来分析捕获的堆转储。
如果您使用的是JDK 1.6或以上版本,您可以使用jmap命令来获取Java进程的堆转储,条件是您应该知道ProcessID。
如果你在Windows机器上,你可以使用任务管理器来获取PID。对于Linux机器,您可以使用各种命令,如ps -A | grep java或netstat -tupln | grep java或top | grep java,这取决于您的应用程序。
然后可以使用类似jmap -dump:format=b,file=sample_heap_dump的命令。hprof 1234,其中1234是PID。
有多种工具可用于解释hprof文件。我将推荐使用简单的Oracle的visualvm工具。
下面的java代码通过提供一个远程进程的PID来获取java进程的堆转储。该程序使用远程JMX连接转储堆到一个文件。它可能对某人有帮助。不需要jmap。
import java.lang.management.ManagementFactory;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.reflect.Method;
public class HeapDumper {
public static final String HOST = "192.168.11.177";
public static final String PORT = "1600";
public static final String FILE_NAME = "heapDump.hprof";
public static final String FOLDER_PATH = "C:/";
private static final String HOTSPOT_BEAN_NAME ="com.sun.management:type=HotSpotDiagnostic";
public static void main(String[] args) {
if(args.length == 0) {
System.out.println("Enter PID of the Java Process !!!");
return;
}
String pidString = args[0];
int pid = -1;
if(pidString!=null && pidString.length() > 0) {
try {
pid = Integer.parseInt(pidString);
}
catch(Exception e) {
System.out.println("PID is not Valid !!!");
return;
}
}
boolean isHeapDumpSuccess = false;
boolean live = true;
if(pid > 0) {
MBeanServerConnection beanServerConn = getJMXConnection();
if(beanServerConn!=null) {
Class clazz = null;
String dumpFile = FOLDER_PATH+"/"+FILE_NAME;
try{
clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
Object hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(beanServerConn, HOTSPOT_BEAN_NAME, clazz);
Method method = clazz.getMethod("dumpHeap", new Class[]{String.class , boolean.class});
method.setAccessible(true);
method.invoke(hotspotMBean , new Object[] {dumpFile, new Boolean(live)});
isHeapDumpSuccess = true;
}
catch(Exception e){
e.printStackTrace();
isHeapDumpSuccess = false;
}
finally{
clazz = null;
}
}
}
if(isHeapDumpSuccess){
System.out.println("HeapDump is Success !!!");
}
else{
System.out.println("HeapDump is not Success !!!");
}
}
private static MBeanServerConnection getJMXConnection() {
MBeanServerConnection mbeanServerConnection = null;
String urlString = "service:jmx:rmi:///jndi/rmi://" + HOST + ":" + PORT + "/jmxrmi";
try {
JMXServiceURL url = new JMXServiceURL(urlString);
JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
mbeanServerConnection = jmxConnector.getMBeanServerConnection();
System.out.println("JMX Connection is Success for the URL :"+urlString);
}
catch(Exception e) {
System.out.println("JMX Connection Failed !!!");
}
return mbeanServerConnection;
}
}