我有一个Java应用程序,我从控制台运行,而控制台又执行另一个Java进程。我想获得该子进程的线程/堆转储。
在Unix上,我可以做一个kill -3 <pid>
但是在Windows AFAIK上获取线程转储的唯一方法是在控制台中使用Ctrl-Break。但这只会让我转移父进程,而不是孩子。
是否有其他方法可以获得堆转储?
答案 0 :(得分:356)
假设您知道jmap
,您可以使用pid
获取正在运行的任何进程的转储。
使用任务管理器或资源监视器获取pid
。然后
jmap -dump:format=b,file=cheap.bin <pid>
获取该进程的堆。
答案 1 :(得分:110)
您混淆了两个不同的Java转储。 kill -3
生成线程转储,而不是堆转储。
线程转储=将JVM输出中的每个线程的堆栈跟踪作为文本输出到stdout。
堆转储= JVM进程输出到二进制文件的内存内容。
要在Windows上进行线程转储, CTRL + BREAK ,如果您的JVM是前台进程是最简单的方法。如果你在Windows上有类似unix的shell,如Cygwin或MobaXterm,你可以像在Unix中一样使用kill -3 {pid}
。
要在Unix中进行线程转储, CTRL + C 如果您的JVM是前台进程,或只要您获得正确的kill -3 {pid}
就可以工作JVM的PID。
使用任何一个平台,Java都有几个可以提供帮助的实用程序。对于线程转储,jstack {pid}
是您最好的选择。 http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html
只是为了完成转储问题:堆转储不常用,因为它们难以解释。但是,如果你知道在哪里/如何看待它们,它们中有很多有用的信息。最常见的用法是查找内存泄漏。最好在java命令行上设置-D
,以便在OutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError
上自动生成堆转储但是,您也可以手动触发堆转储。最常见的方法是使用java实用程序jmap
。
注意:此实用程序并非在所有平台上都可用。从JDK 1.6开始,Windows上可以使用jmap
。
示例命令行看起来像
jmap -dump:file=myheap.bin {pid of the JVM}
输出“myheap.bin”不是人类可读的(对于我们大多数人来说),你需要一个工具来分析它。我的偏好是MAT。 http://www.eclipse.org/mat/
答案 2 :(得分:30)
我认为在Linux进程中创建.hprof文件的最佳方法是使用 jmap 命令。例如:jmap -dump:format=b,file=filename.hprof {PID}
答案 3 :(得分:18)
除了使用上面提到的jconsole / visualvm之外,您还可以在另一个命令行窗口中使用jstack -l <vm-id>
,并捕获该输出。
&lt; vm-id&gt;可以使用任务管理器(它是windows和unix上的进程ID)或使用jps
找到。
Sun JDK版本6及更高版本中包含jstack
和jps
。
答案 4 :(得分:16)
我推荐使用JDK(jvisualvm.exe)分发的Java VisualVM。它可以动态连接并访问线程和堆。我发现一些问题非常宝贵。
答案 5 :(得分:14)
请尝试以下选项之一。
对于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参数中具有G1GC算法的64位JVM(仅使用G1GC算法生成活动对象堆):
jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
相关的SE问题:Java heap dump error with jmap command : Premature EOF
在此article
查看jmap
的各种选项
答案 6 :(得分:13)
如果你在服务器jre 8及以上,你可以使用它:
jcmd PID GC.heap_dump /tmp/dump
答案 7 :(得分:12)
如果你想在内存不足的情况下使用堆转储,可以使用选项-XX:-HeapDumpOnOutOfMemoryError
启动Java
答案 8 :(得分:6)
您可以运行jconsole
(包含在Java 6的SDK中),然后连接到您的Java应用程序。它将向您显示每个Thread运行及其堆栈跟踪。
答案 9 :(得分:5)
您可以从Cygwin发送kill -3 <pid>
。您必须使用Cygwin ps
选项来查找Windows进程,然后将信号发送到该进程。
答案 10 :(得分:4)
您必须将第二个java可执行文件的输出重定向到某个文件。 然后,使用SendSignal与send "-3"进行第二个流程。
答案 11 :(得分:3)
如果您使用的是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。
有多种tool available来解释hprof文件。我将推荐Oracle的visualvm工具,它易于使用。
答案 12 :(得分:2)
我为Java 8(使用PsExec
和jcmd
)编写了一个名为jvmdump.bat
的小批处理脚本,它转储了线程,堆,系统属性和JVM args。
:: 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
中启动命令提示符并从那里运行它。 e.g。
%PsExec% -s -h -d -i 0 cmd.exe
这会在交互式会话中提示您(单击底部的任务栏图标)到View the message
,这会将您带到另一个会话中的新控制台,您可以从中运行jvmdump.bat
脚本。
答案 13 :(得分:2)
如何获取Java应用程序的进程ID?
执行命令“ jcmd”以获取Java应用程序的进程ID。
如何获取线程转储?
jcmd PID Thread.print> thread.dump
参考link
您甚至可以使用jstack获取线程转储(jstack PID> thread.dump)。参考link
如何获取堆转储?
使用jmap工具获取堆转储。 jmap -F -dump:live,format = b,file = heap.bin PID
PID代表应用程序的进程ID。参考link
答案 14 :(得分:1)
如果由于某种原因您不能(或不想)使用控制台/终端,则有另一种解决方案。您可以使Java应用程序为您打印线程转储。收集堆栈跟踪的代码相当简单,可以附加到按钮或Web界面。
private static String getThreadDump() {
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
StringBuilder out = new StringBuilder();
for (Map.Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet()) {
Thread thread = entry.getKey();
StackTraceElement[] elements = entry.getValue();
out.append(String.format("%s | prio=%d | %s", thread.getName(), thread.getPriority(), thread.getState()));
out.append('\n');
for (StackTraceElement element : elements) {
out.append(element.toString()).append('\n');
}
out.append('\n');
}
return out.toString();
}
此方法将返回如下所示的字符串:
main | prio=5 | RUNNABLE
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1607)
Main.getThreadDump(Main.java:8)
Main.main(Main.java:36)
Monitor Ctrl-Break | prio=5 | RUNNABLE
java.net.PlainSocketImpl.initProto(Native Method)
java.net.PlainSocketImpl.<clinit>(PlainSocketImpl.java:45)
java.net.Socket.setImpl(Socket.java:503)
java.net.Socket.<init>(Socket.java:424)
java.net.Socket.<init>(Socket.java:211)
com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:59)
Finalizer | prio=8 | WAITING
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Reference Handler | prio=10 | WAITING
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:502)
java.lang.ref.Reference.tryHandlePending(Reference.java:191)
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
对于那些对带有流的Java 8版本感兴趣的人,代码更加紧凑:
private static String getThreadDump() {
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
StringBuilder out = new StringBuilder();
allStackTraces.forEach((thread, elements) -> {
out.append(String.format("%s | prio=%d | %s", thread.getName(), thread.getPriority(), thread.getState()));
out.append('\n');
Arrays.stream(elements).forEach(element -> out.append(element.toString()).append('\n'));
out.append('\n');
});
return out.toString();
}
您可以使用以下方法轻松测试此代码:
System.out.print(getThreadDump());
答案 15 :(得分:0)
Visualvm后续行动:
如果由于未使用正确的JVM参数(并且位于远程框上)启动而无法从jvisualvm连接到正在运行的JVM,请在远程框中运行tooltip
,然后,假设您具有直接连接,则将其添加为visualvm中的“远程主机”,双击主机名,该框中的所有其他JVM都会神奇地显示在visualvm中。
如果没有“直接连接”到该盒子上的端口,则也可以通过proxy进行操作。
一旦您看到所需的过程,请在jvisualvm中进行深入研究,然后使用监视器选项卡->“堆转储”按钮。
答案 16 :(得分:0)
下面的Java代码用于通过提供PID来获取Java进程的堆转储。本程序使用远程JMX连接来转储堆。对于某些人可能会有所帮助。
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;
}
}
答案 17 :(得分:0)
也许是jcmd?
Jcmd 实用程序用于向JVM发送诊断命令请求,这些请求对于控制Java Flight Recording,故障排除和诊断JVM和Java应用程序很有用。
jcmd工具是随Oracle Java 7引入的,在通过使用Java标识Java进程的ID(类似于jps),获取堆转储(类似于jmap),获取线程转储(对JVM应用程序进行故障诊断)中特别有用。类似于jstack),查看虚拟机特征,例如系统属性和命令行标志(类似于jinfo),以及获取垃圾回收统计信息(类似于jstat)。 jcmd工具被称为“用于调查和解决JVM应用程序问题的瑞士军刀”和“隐藏的宝石”。
以下是调用jcmd
时需要使用的过程:
jcmd <pid> GC.heap_dump <file-path>
查看有关服用Java heap dump的更多信息。
答案 18 :(得分:0)
为了从Windows的子Java进程中进行线程转储/堆转储,您需要将子进程ID标识为第一步。
通过发出命令: jps ,您将可以获得在Windows计算机上运行的所有Java进程ID。从此列表中,您需要选择子进程ID。拥有子进程ID后,可以使用各种选项来捕获线程转储和堆转储。
捕获线程转储:
有8个捕获线程转储的选项:
有关每个选项的详细信息,请参见in this article。捕获线程转储后,可以使用诸如fastThread,Samuraito之类的工具来分析线程转储。
捕获堆转储:
有7种捕获堆转储的选项:
jmap
-XX:+ HeapDumpOnOutOfMemoryError
jcmd
JVisualVM
JMX
程序化方法
管理控制台
有关每个选项的详细信息,请参见in this article。捕获堆转储后,可以使用Eclipse Memory Analysis tool,HeapHero之类的工具来分析捕获的堆转储。
答案 19 :(得分:-1)
在Oracle JDK上,我们有一个名为jmap的命令(在Java Home的bin文件夹中可用)。 该命令的用法如下
jmap(选项)(pid)
示例:jmap -dump:live,format = b,file = heap.bin(pid)