我正在考虑为我的Java应用程序创建一个调试工具。
我想知道是否有可能获得堆栈跟踪,就像Exception.printStackTrace()
但没有实际抛出异常一样?
我的目标是,在任何给定的方法中,转储堆栈以查看方法调用者是谁。
答案 0 :(得分:211)
是的,只需使用
即可Thread.dumpStack()
答案 1 :(得分:74)
您还可以尝试Thread.getAllStackTraces()
获取所有活动线程的堆栈跟踪图。
答案 2 :(得分:36)
如果您只想要当前线程的跟踪(而不是系统中的所有线程,如Ram的建议那样),请执行以下操作:
Thread.currentThread()。getStackTrace()
要找到来电者,请执行:
private String getCallingMethodName() {
StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
return callingFrame.getMethodName();
}
从需要知道其调用者是谁的方法中调用该方法。但是,请注意:列表中调用帧的索引可能因JVM而异!这一切都取决于在生成跟踪之前getStackTrace中有多少层调用。一个更强大的解决方案是获取跟踪,并迭代它以查找getCallingMethodName的帧,然后再向前走两步以找到真正的调用者。
答案 3 :(得分:31)
您可以获得这样的堆栈跟踪:
Throwable t = new Throwable();
t.printStackTrace();
如果要访问该帧,可以使用t.getStackTrace()来获取堆栈帧数组。
请注意,如果热点编译器忙于优化,那么这个堆栈跟踪(就像任何其他堆栈跟踪一样)可能会丢失一些帧。
答案 4 :(得分:13)
请注意,Thread.dumpStack()实际上会引发异常:
new Exception("Stack trace").printStackTrace();
答案 5 :(得分:6)
您还可以向JVM发送信号,通过向进程发送QUIT信号,对正在运行的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 :(得分:4)
如果您需要捕获输出
StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
答案 7 :(得分:3)
Java 9引入了StackWalker
和用于遍历堆栈的支持类。
以下是Javadoc的几个片段:
walk
方法为当前线程打开StackFrames
的顺序流,然后应用给定的函数来遍历module AsyncResult = let liftAsync x = async { return x } let pure (value: 'a) : Async<Result<'a, 'b>> = async { return Ok value } let returnFrom (value: Async<Result<'a, 'b>>) : Async<Result<'a, 'b>> = value let bind (binder: 'a -> Async<Result<'b, 'c>>) (asyncResult: Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> = async { let! result = asyncResult match result with | Ok x -> return! binder x | Error x -> return! Error x |> liftAsync } let bindResult (binder: 'a -> Async<Result<'b, 'c>>) (result: Result<'a, 'c>) : Async<Result<'b, 'c>> = bind binder (liftAsync result) let bindAsync (binder: 'a -> Async<Result<'b, 'c>>) (asnc: Async<'a>) : Async<Result<'b, 'c>> = bind binder (Async.map Ok asnc) type AsyncResultBuilder() = member __.Return value = pure value member __.ReturnFrom value = returnFrom value member __.Bind (asyncResult, binder) = bind binder asyncResult member __.Bind (result, binder) = bindResult binder result member __.Bind (async, binder) = bindAsync binder async let asyncResult = AsyncResultBuilder() // Usage let functionReturningAsync () = async { return 2 } let errorHandlingFunction () = asyncResult { // Error: A unique overload for method 'Bind' could not be determined ... do! functionReturningAsync() }
流。流按顺序报告堆栈帧元素,从表示生成堆栈的执行点的最顶部帧到最底部的帧。当walk方法返回时,StackFrame
流将关闭。如果尝试重用已关闭的流,则会抛出StackFrame
。...
- 醇>
快照当前线程的前10个堆栈帧
IllegalStateException
答案 8 :(得分:3)
如果您愿意输出到stderr
,则answer by Rob Di Marco是最好的。
如果要捕获到String
,并按照常规跟踪添加新行,则可以执行以下操作:
Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
答案 9 :(得分:0)
您甚至不必在代码中执行此操作。您可以将Java HPROF附加到您的进程,并且在任何时候点击Control- \以输出堆转储,运行线程等。 。 。没有破坏你的应用程序代码。这有点过时了,Java 6附带了GUI jconsole,但我仍然发现HPROF非常有用。