有没有办法在不抛出异常的情况下转储堆栈跟踪?
Posted
技术标签:
【中文标题】有没有办法在不抛出异常的情况下转储堆栈跟踪?【英文标题】:Is there a way to dump a stack trace without throwing an exception in java? 【发布时间】:2010-10-31 00:11:02 【问题描述】:我正在考虑为我的 Java 应用程序创建一个调试工具。
我想知道是否可以获得堆栈跟踪,就像Exception.printStackTrace()
但实际上没有抛出异常?
我的目标是,在任何给定的方法中,转储堆栈以查看方法调用者是谁。
【问题讨论】:
【参考方案1】:是的,只需使用
Thread.dumpStack()
【讨论】:
这才是真正解决问题的答案。 非常简洁优雅。这应该是公认的答案。 Fwiw,这个方法的来源:public static void dumpStack() new Exception("Stack trace").printStackTrace();
如果您对输出到stderr
感到满意,这是迄今为止问题的最佳答案,这似乎是问题的含义。【参考方案2】:
您也可以尝试Thread.getAllStackTraces()
获取所有活动线程的堆栈跟踪图。
【讨论】:
【参考方案3】:如果您只想跟踪当前线程(而不是系统中的所有线程,如 Ram 的建议),请执行以下操作:
Thread.currentThread().getStackTrace()
要查找调用者,请执行以下操作:
private String getCallingMethodName()
StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
return callingFrame.getMethodName();
然后从需要知道调用者是谁的方法中调用该方法。但是,请注意:列表中调用帧的索引可能会因 JVM 而异!这完全取决于在到达生成跟踪点之前 getStackTrace 中有多少层调用。一个更强大的解决方案是获取跟踪,并对其进行迭代以查找 getCallingMethodName 的框架,然后再往前走两步以找到真正的调用者。
【讨论】:
所以只需自己创建一个新的异常并使用[1]作为索引! 这个问题的标题是“不抛出异常”。当然,您建议创建异常但不抛出它,但我仍然认为这不符合问题的精神。 当然是。创建异常是获取堆栈跟踪的最底层方法。甚至你的 Thread.currentThread().getStackTrace() 也能做到,但我的方法做得更快:)。 @Daniel 还有另一个考虑:有许多测试框架,例如Spock,只需创建一个异常(不抛出它)就足以让框架接受:然后测试将认为异常已经“发生”并且测试将以失败结束。 @mikerodent:你确定吗? Spock 将不得不使用代理来做到这一点。但无论如何,因为我只需要调用类,所以我在实用程序函数中使用了 Reflection.getCallerClass(2)。你不会这样得到函数和行号,想。【参考方案4】:你可以获得这样的堆栈跟踪:
Throwable t = new Throwable();
t.printStackTrace();
如果要访问帧,可以使用 t.getStackTrace() 获取堆栈帧数组。
请注意,如果热点编译器一直忙于优化,此堆栈跟踪(就像任何其他堆栈跟踪一样)可能会丢失一些帧。
【讨论】:
我喜欢这个答案,因为它为我提供了指导输出的机会。我可以用t.printStackTrace(System.out)
替换t.printStackTrace
。
一行:new Throwable().printStackTrace(System.out);
这应该是公认的答案。这很简单直接!感谢分享
并且很容易发送到java.util.Logger log.log(Level.SEVERE, "Failed to do something", new Throwable());
【参考方案5】:
注意 Thread.dumpStack() 实际上抛出了一个异常:
new Exception("Stack trace").printStackTrace();
【讨论】:
它会创建一个异常,但不会抛出它。【参考方案6】:Java 9 引入了 StackWalker
和支持类来遍历堆栈。
这里有一些来自 Javadoc 的 sn-ps:
walk
方法为当前线程打开StackFrames
的顺序流,然后应用给定函数遍历StackFrame
流。该流按顺序报告堆栈帧元素,从表示生成堆栈的执行点的最顶部帧到最底部帧。当 walk 方法返回时,StackFrame
流将关闭。如果尝试重用关闭的流,IllegalStateException
将被抛出。...
要快照当前线程的前 10 个堆栈帧,
List<StackFrame> stack = StackWalker.getInstance().walk(s -> s.limit(10).collect(Collectors.toList()));
【讨论】:
【参考方案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
【讨论】:
【参考方案8】:如果你需要捕获输出
StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
【讨论】:
【参考方案9】:如果您愿意输出到stderr
,answer by Rob Di Marco 是迄今为止最好的。
如果您想捕获到String
,按照正常跟踪使用新行,您可以这样做:
Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
【讨论】:
【参考方案10】:HPROF Java Profiler
您甚至不必在代码中执行此操作。您可以将 Java HPROF 附加到您的进程,并随时点击 Control-\ 以输出堆转储、正在运行的线程等。 . .无需破坏您的应用程序代码。这有点过时了,Java 6 自带了 GUI jconsole,但我还是觉得 HPROF 很有用。
【讨论】:
以上是关于有没有办法在不抛出异常的情况下转储堆栈跟踪?的主要内容,如果未能解决你的问题,请参考以下文章
我可以在不抛出异常的情况下测试正则表达式在 C# 中是不是有效吗
Android - 在没有堆栈跟踪的情况下调用 getJSONArray 抛出 JSONException
如何在不抛出 TaskCanceledExceptions 的情况下等待任务?