我正在考虑为我的 Java 应用程序创建一个调试工具。
我想知道是否有可能获得堆栈跟踪,就像
Exception.printStackTrace()
但实际上没有抛出异常?
我的目标是,在任何给定的方法中,转储堆栈以查看方法调用者是谁。
是的,只需使用
Thread.dumpStack()
您还可以尝试
Thread.getAllStackTraces()
获取所有活动线程的堆栈跟踪图。
如果您只想跟踪当前线程(而不是像 Ram 的建议那样跟踪系统中的所有线程),请执行以下操作:
Thread.currentThread().getStackTrace()
要找到呼叫者,请执行以下操作:
private String getCallingMethodName() {
StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
return callingFrame.getMethodName();
}
并从需要知道其调用者是谁的方法中调用该方法。但是,请注意:列表中调用框架的索引可能会根据 JVM 的不同而有所不同!这完全取决于在到达生成跟踪的点之前 getStackTrace 中有多少层调用。更可靠的解决方案是获取跟踪,并迭代它以查找 getCallingMethodName 的框架,然后进一步向上两步找到真正的调用者。
您可以获得这样的堆栈跟踪:
Throwable t = new Throwable();
t.printStackTrace();
如果要访问帧,可以使用
t.getStackTrace()
获取堆栈帧数组。
请注意,如果热点编译器一直忙于优化事物,则此堆栈跟踪(就像任何其他堆栈跟踪一样)可能会丢失一些帧。
注意 Thread.dumpStack() 实际上抛出了一个异常:
new Exception("Stack trace").printStackTrace();
StackWalker
以及用于遍历堆栈的支持类。
以下是 Javadoc 中的一些片段:
方法为当前线程打开walk
的顺序流,然后应用给定的函数来遍历StackFrames
流。该流按顺序报告堆栈帧元素,从表示生成堆栈的执行点的最顶部帧到最底部帧。当 walk 方法返回时,StackFrame
流将关闭。如果尝试重用已关闭的流,则会抛出StackFrame
。IllegalStateException
...
要快照当前线程的前10个堆栈帧,
List<StackFrame> stack = StackWalker.getInstance().walk(s -> s.limit(10).collect(Collectors.toList()));
您还可以通过向进程发送 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
如果您需要捕获输出
StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
stderr
,那么 Rob Di Marco 的
答案是迄今为止最好的。如果您想捕获到
String
,并按照正常轨迹使用新行,您可以这样做:
Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );