我有一个从控制台运行的Java应用程序,该应用程序反过来执行另一个Java进程。我想获得该子进程的线程/堆转储。
在Unix上,我可以执行kill -3 <pid>,但在Windows AFAIK上,获得线程转储的唯一方法是在控制台中按Ctrl-Break。但这只给了我父进程的转储,而不是子进程的转储。
有其他方法来获取堆转储吗?
我有一个从控制台运行的Java应用程序,该应用程序反过来执行另一个Java进程。我想获得该子进程的线程/堆转储。
在Unix上,我可以执行kill -3 <pid>,但在Windows AFAIK上,获得线程转储的唯一方法是在控制台中按Ctrl-Break。但这只给了我父进程的转储,而不是子进程的转储。
有其他方法来获取堆转储吗?
当前回答
您必须将输出从第二个java可执行文件重定向到某个文件。 然后,使用SendSignal向第二个进程发送“-3”。
其他回答
我推荐JDK附带的Java VisualVM (jvisualvm.exe)。它可以动态连接并访问线程和堆。我发现在某些问题上它是无价的。
如果由于某些原因不能(或不想)使用控制台/终端,还有另一种解决方案。您可以让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());
您可以使用jmap获取正在运行的任何进程的转储,假设您知道pid。
使用任务管理器或资源监视器获取pid。然后
jmap -dump:format=b,file=heap.hprof <pid>
获取该进程的堆。
对于安装了bash和pgrep并且正在运行单个Java进程的系统,请尝试:
jmap -dump:format=b,file=heap.hprof $(pgrep java)
您可以运行jconsole(包含在Java 6的SDK中),然后连接到Java应用程序。它将显示每个正在运行的线程及其堆栈跟踪。
如何获取java应用程序的进程id ?
执行“jcmd”命令可获取java应用程序的进程id。
如何获得线程转储?
jcmd PID线程。打印>线程
参考链接
您甚至可以使用jstack来获取线程转储(jstack PID > thread.dump)。参考链接
如何获得堆转储?
使用jmap工具获取堆转储。 jmap -F -dump:live,format=b,file=heap.bin PID
PID:应用程序的进程号。参考链接