Java中如何获取当前的堆栈跟踪?

1117

如何在Java中获取当前堆栈跟踪,就像在.NET中你可以使用Environment.StackTrace一样?

我找到了Thread.dumpStack(),但这不是我想要的 - 我想要获取堆栈跟踪,而不是将其打印出来。


78
在Eclipse调试时,我经常使用"new Exception().printStackTrace()"作为观察表达式。当你在断点处暂停时,这非常方便,可以知道你的代码执行到哪里。 - Tim Büthe
24
@TimBüthe:当你处于调试模式时,Eclipse不是已经告诉你堆栈跟踪了吗?我想是这样的。 (翻译):在调试模式下,Eclipse是否已经向您显示了堆栈跟踪信息? 我认为是这样的。 - Luigi Massa Gallerano
49
Arrays.toString(Thread.currentThread().getStackTrace()); 简单明了。 - Ajay
2
@Arkanon 这是关于Java的,它展示了在.NET中如何实现以及在Java中的等效方法。 - Pokechu22
13
为了漂亮地打印它,您可以使用Apache StringUtils:StringUtils.join(currentThread().getStackTrace(), "\n"); - Art
显示剩余2条评论
23个回答

1288

52
如果您已经在使用Apache Commons获取字符串,则这是一个简洁的单行代码:String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e);。该代码可以获得完整的堆栈跟踪信息。 - ripper234
5
数组中的第一个元素(索引为0)是java.lang.Thread.getStackTrace方法,第二个元素(索引为1)通常是我们关注的第一个。 - lilalinux
230
将堆栈跟踪转换为字符串的简短代码(当没有异常并且不使用Apache Arrays.toString(Thread.currentThread().getStackTrace())时可用): - Tony
10
@Tony的解决方案更好,因为它不需要一个可抛出的对象。 - mvd
8
如下许多答案所指出的那样 - new Throwable().getStackTrace() 更快,因为它不需要检查 this != Thread.currentThread(),并且避免了通过子类(Exception extends Throwable)调用可能存在的JVM开销。 - Vlad
显示剩余5条评论

293
StackTraceElement[] st = Thread.currentThread().getStackTrace();

如果你不关心堆栈的第一个元素是什么,那么这种方法就可以。

StackTraceElement[] st = new Throwable().getStackTrace();

如果这很重要,那么您当前方法的位置将被定义。


42
(new Throwable()).getStackTrace() 的执行速度更快(参见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302)。 - MightyE
2
你能详细解释一下Thread.currentThread().getStackTrace()和new Throwable().getStackTrace()的结果可能有什么不同吗?我尝试了两种方法,发现Thread.currentThread().getStackTrace()返回的数组中Thread.getStackTrace在索引0处,调用方法在索引1处。而new Throwable().getStackTrace()方法返回的数组中调用方法在索引0处。看起来这两种方法都对当前方法有一个定义好的位置,但是位置不同。你还指出了其他的区别吗? - Ryan
9
@Ryan,不能保证Thread.currentThread().getStackTrace()在方法的不同版本中始终保持相同的位置。因此,当你检查时它可能在索引1上。在某些JVM版本(1.5或1.6,记不清了),对于Sun而言,它在索引2上。这就是我所说的定义位置——规范要求它应该在那里,并且将来也应该在那里保持不变。 - Yishai
5
@MightyE 这个 bug 现在已经被报告为在 Java 6 中已经修复了。从内部看,当请求的堆栈跟踪在当前线程上时(对于 Thread.currentThread().getStackTrace() 来说总是这种情况),它现在会执行 (new Exception()).getStackTrace() - M. Justin
4
@MJustin说这个漏洞并不是“现在”才报告已经被修复,它实际上早在六年前就已经被修复了,事实上,这个漏洞甚至在Stackoverflow成立之前就已经被修复了。 - Holger
显示剩余4条评论

204
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
    System.out.println(ste + "\n");
}

23
Java中的Throwable类定义了printStackTrace方法,你可以使用new Exception().printStackTrace(System.out)来调用。我记得for循环可能会有更少的开销,但你应该只在调试时使用它。 - Jaap
2
我最喜欢这个方法,因为你可以通过包装println语句来消除噪音,例如:for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { if(ste.toString().contains("mypackages")){ System.out.println(ste); } } - nby

66

Tony在接受的回答中发表了评论,提供了似乎是最好的答案,实际上回答了原帖作者的问题:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );

...这位 OP 并没有询问如何从 Exception 的堆栈跟踪中获取 String 。虽然我非常喜欢 Apache Commons,但当有像上面那样简单的东西时,没有逻辑上的理由使用外部库。


我有一个线程列表,需要打印它们的堆栈跟踪。 - Daniil Iaitskov
在这种情况下,请确保使用Thread.getAllStackTraces(),它会给你一个Map<Thread, StackTraceElement[]>。当Hotspot需要safepoint时,它将safepoint所有线程。Zing可以将单个线程带到safepoint而不干扰其他线程。获取堆栈跟踪需要一个safepoint。Thread.getAllStackTraces()只获取所有堆栈跟踪并仅停止所有线程一次。当然,您必须找出您实际感兴趣的映射。 - Ralf H

65
Thread.currentThread().getStackTrace();

JDK1.5开始提供此功能。

对于较旧版本,您可以将exception.printStackTrace()重定向到StringWriter()

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

4
new Throwable().getStackTrace()自JDK1.4以来就可用。 - Holger

42

你可以使用Apache的commons库来实现:

String fullStackTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);

13
这是一个简洁的解决方案。供使用 org.apache.commons.lang3 的任何人参考,完整代码为:org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e); - fileoffset
2
只是为了重申fileoffset的评论,当转移到org.apache.commons.lang3时,我最初忽略了--导入使用lang3而不是lang,并用getStackTrace替换了getFullStackTrace - ggkmath
非常有用!在使用IDE(例如IntelliJ IDEA)进行调试时,使用ExceptionUtils.getStackTrace(new Throwable())表达式获取堆栈跟踪非常棒。 - Sergey Vyacheslavovich Brunov

25

在Android上更简单的方法是使用以下代码:

import android.util.Log;
String stackTrace = Log.getStackTraceString(exception); 

4
getStackTraceString是在哪里定义的?这是来自第三方日志记录库吗? - skiphoppy
6
android.util.Log.getStackTraceString是一个Android SDK提供的方法,用于获取异常堆栈信息并将其转换为字符串表示形式。您可以在http://developer.android.com/reference/android/util/Log.html上找到有关此方法的更多信息。 - Ondrej Kvasnovsky

24

另一个解决方案(仅 35 31 个字符):

new Exception().printStackTrace();   
new Error().printStackTrace();

1
很棒的解决方案。现在我不必遍历所有堆栈帧了。 - Vineel

22
要获取所有线程的堆栈跟踪,您可以使用jstack实用程序、JConsole或发送kill-quit信号(在Posix操作系统上)。 但是,如果您想以编程方式执行此操作,可以尝试使用ThreadMXBean:
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(true, true);

for (ThreadInfo info : infos) {
  StackTraceElement[] elems = info.getStackTrace();
  // Print out elements, etc.
}

如上所述,如果您只想获取当前线程的堆栈跟踪信息,则更容易-只需使用Thread.currentThread().getStackTrace()


2
此答案中的代码片段最接近于以编程方式生成您在向JVM发送QUIT信号(在类Unix系统上)或Windows上的<ctrl><break>时看到的转储。与其他答案不同,您可以看到监视器和同步器以及堆栈跟踪(例如,如果您遍历StackTraceElement数组并在每个元素上执行System.err.println(elems[i]))。片段中的第二行缺少bean.dumpAllThreads之前,但我想大多数人都能解决这个问题。 - George Hawkins

18
我建议:
  Thread.dumpStack()

这是一种更简单的方式,因为它有一个优点,即当可能根本不存在问题时,不需要构建异常或可抛出对象,并且这种方法更加直接。


1
好的,实际上,dumpStack()是一个静态方法,应该以静态方式访问。谢谢,Eclipse! - Thomas Adkins
5
我喜欢这个快捷方式,但是这个方法的源代码是:new Exception("Stack trace").printStackTrace();,因此实际上它正在构建一个Throwable。 - Florent
1
问题是:“我找到了Thread.dumpStack(),但这不是我想要的——我想要获取堆栈跟踪,而不是将其打印出来。” - Manuel Romeiro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接