有没有一种方法可以转储堆栈跟踪而不会在Java中引发异常?


159

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

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

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

Answers:


84

您还可以尝试Thread.getAllStackTraces()获取所有活动线程的堆栈跟踪图。


250

是的,只需使用

Thread.dumpStack()

44
...但是不会扔掉。
罗布2009年

5
那就是真正解决这个问题的答案。
bruno 2015年

7
非常简单而优雅。这应该是公认的答案。
解锁

11
Fwiw,此方法的来源: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK,

如果您对输出到感到满意,这是迄今为止对该问题的最佳答案stderr,这似乎是该问题的含义。
麦克啮齿动物

46

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

Thread.currentThread()。getStackTrace()

要查找呼叫者,请执行以下操作:

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

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


2
因此,只需自己创建一个新的Exception,然后使用[1]作为索引!
丹尼尔(Daniel)

3
该问题的标题为“没有引发异常”。当然,您建议创建异常,但不要抛出该异常,但是我仍然认为这不是问题的实质。
汤姆·安德森

1
当然是的。创建异常是获取堆栈跟踪的最底层方法。甚至您的Thread.currentThread()。getStackTrace()都可以做到,但是我的方法更快:)。
丹尼尔(Daniel)

@Daniel还有一个考虑因素:对于许多测试框架,例如Spock,只需创建一个异常(不抛出异常)就足以使该框架接受:测试将认为“发生了”异常,并且测试将最后,失败了。
mike rodent,

@mikerodent:确定吗?Spock必须使用代理来执行此操作。但是无论如何,因为我只需要调用类,所以我在实用程序函数中使用了Reflection.getCallerClass(2)。想,您不会以这种方式获得功能和行号。
丹尼尔(Daniel)

37

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

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

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

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


我喜欢这个答案,因为它为我提供了指导输出的机会。我可以替换t.printStackTracet.printStackTrace(System.out)

4
在一行中:new Throwable().printStackTrace(System.out);

1
这应该是公认的答案。简单而直接!感谢分享
山姆

2
并且很容易发送给java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead

13

注意,Thread.dumpStack()实际上会引发异常:

new Exception("Stack trace").printStackTrace();

12
它创建一个异常,但不会抛出该异常。
MatrixFrog 2014年

6

您还可以通过向该进程发送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


6

如果需要捕获输出

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();

6

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

以下是Javadoc的一些摘要:

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

...

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

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));

0

HPROF Java Profiler

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


0

如果您愿意输出到Rob Di Marco答案是最好的stderr

如果您要捕获到String,并按照常规跟踪添加新行,则可以执行以下操作:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.