187

我正在考虑为我的 Java 应用程序创建一个调试工具。

我想知道是否可以获得堆栈跟踪,就像Exception.printStackTrace()但实际上没有抛出异常一样?

我的目标是,在任何给定的方法中,转储堆栈以查看方法调用者是谁。

4

10 回答 10

293

是的,只需使用

Thread.dumpStack()
于 2009-06-03T14:15:43.257 回答
86

您还可以尝试Thread.getAllStackTraces()获取所有存活线程的堆栈跟踪图。​​​​​​​</p>

于 2009-06-03T14:19:44.480 回答
52

如果您只想跟踪当前线程(而不是系统中的所有线程,如 Ram 的建议),请执行以下操作:

线程.currentThread()。获取堆栈跟踪()

要查找来电者,请执行以下操作:

private String getCallingMethodName() {
    StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
    return callingFrame.getMethodName();
}

并从需要知道调用者是谁的方法中调用该方法。但是,请注意:列表中调用帧的索引可能会因 JVM 而异!这完全取决于在您到达生成跟踪点之前 getStackTrace 中有多少层调用。一个更健壮的解决方案是获取跟踪,并对其进行迭代以查找 getCallingMethodName 的框架,然后再往前走两步以找到真正的调用者。

于 2009-06-03T15:28:37.600 回答
41

您可以获得这样的堆栈跟踪:

Throwable t = new Throwable();
t.printStackTrace();

如果要访问帧,可以使用 t.getStackTrace() 获取堆栈帧数组。

请注意,如果热点编译器一直忙于优化,这个堆栈跟踪(就像任何其他的一样)可能会丢失一些帧。

于 2009-06-03T14:16:12.023 回答
14

注意 Thread.dumpStack() 实际上抛出了一个异常:

new Exception("Stack trace").printStackTrace();
于 2013-01-31T15:06:20.750 回答
10

Java 9 引入了StackWalker用于遍历堆栈的类和支持类。

以下是 Javadoc 中的一些片段:

walk方法为当前线程打开一个顺序流,StackFrames然后应用给定的函数来遍历StackFrame流。该流按顺序报告堆栈帧元素,从表示生成堆栈的执行点的最顶部帧到最底部帧。当StackFramewalk 方法返回时,流将关闭。如果试图重用关闭的流,IllegalStateException将被抛出。

...

  1. 要快照当前线程的前 10 个堆栈帧,

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));
    
于 2017-12-07T14:16:03.140 回答
7

您还可以通过向进程发送 QUIT 信号向 JVM 发送信号以在正在运行的 Java 进程上执行 Thread.getAllStackTraces()。

在 Unix/Linux 上使用:

kill -QUIT process_id,其中 process_id 是您的 Java 程序的进程号。

在 Windows 上,您可以在应用程序中按 Ctrl-Break,尽管您通常不会看到这个,除非您正在运行控制台进程。

JDK6 引入了另一个选项 jstack 命令,它将显示计算机上任何正在运行的 JDK6 进程的堆栈:

jstack [-l] <pid>

这些选项对于在生产环境中运行且不能轻易修改的应用程序非常有用。它们对于诊断运行时死锁或性能问题特别有用。

http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/ http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html

于 2009-06-03T16:04:38.690 回答
7

如果您需要捕获输出

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
于 2017-04-04T10:50:27.040 回答
5

Rob Di Marco的答案是迄今为止最好的,如果您愿意输出到stderr.

如果您想String按照正常跟踪使用新行捕获到 a ,您可以执行以下操作:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
于 2020-05-02T08:28:11.973 回答
0

HPROF Java Profiler

您甚至不必在代码中执行此操作。您可以将 Java HPROF 附加到您的进程,并随时点击 Control-\ 以输出堆转储、正在运行的线程等。. . 无需破坏您的应用程序代码。这有点过时了,Java 6 带有 GUI jconsole,但我仍然发现 HPROF 非常有用。

于 2009-06-03T15:38:27.420 回答