访问堆栈跟踪具有良好的性能?

u6f*_*f6o 5 java performance stack-trace

我们最近应用了几乎所有应用程序模块/组件(大约50个项目)使用的缓存解决方案.为了更好地了解在不同的系统"位置"上执行哪些缓存操作,我们为当前执行的缓存操作添加了日志记录,包括堆栈跟踪,以准确了解触发缓存操作的内容.

我们当前的方法如下所示:我们从新的Throwable()中获取堆栈跟踪,过滤不相关的行并记录剩余的堆栈跟踪.尽管如此,创建一个新的异常以便记录是不便宜的.由于我们不直接使用缓存,而是通过休眠,因此找不到哪个调用者在没有访问堆栈跟踪的情况下触发了操作就不那么容易了.

我的问题是:是否有一个更高效的解决方案来访问当前的堆栈跟踪然后Throwable().getStackTrace或Thread.currentThread().getStackTrace()?

apa*_*gin 13

实际上获取异常堆栈跟踪并不是那么慢:

  • 只有10%的时间用于收集回溯 - 这是在Throwable()构造函数中完成的;
  • 其他90%的时间用于回溯从内部格式转换为可读数StackTraceElement[]组 - 这是完成的getStackTrace().

这意味着,如果您不需要同步处理堆栈跟踪,则可以只调用new Exception()(这或多或少是快速操作),然后e.getStackTrace()在另一个线程中稍后或异步调用.

此外,有时(如在您的情况下)不需要完整的堆栈跟踪.您可以跳过一些堆栈帧并仅解码您感兴趣的那些.魔术sun.misc.SharedSectets类将有所帮助.

例如,只获得第2帧到第5帧,请使用

Exception e = new Exception();
int depth = Math.min(5, SharedSecrets.getJavaLangAccess().getStackTraceDepth(e));

for (int frame = 2; frame < depth; frame++) {
    StackTraceElement elem = SharedSecrets.getJavaLangAccess().getStackTraceElement(e, frame);
    System.out.println(elem);
}
Run Code Online (Sandbox Code Playgroud)

  • @reallynice对.`SharedSecrets`适用于Java 8.它不适用于Java 9,但是,有新的标准[StackWalker API](http://download.java.net/java/jdk9/docs/api/java/lang /StackWalker.html)适用于Java 9及更高版本. (3认同)

Pet*_*rey 6

Thread.currentThread().getStackTrace() 与@apangin指出的基本相同,但如果它避免创建一个Throwable,它将来会更快.

但是,您可能会发现子采样将为您提供所需的改进.而不是记录每次访问,记录每第N次访问.如果你记录每10次访问,你可以减少高达90%的开销,如果你有大量的访问,它将几乎一样准确.

另一种选择是使用像YourKit这样的分析器,它可以更有效地完成这项工作.这可以显示方法及其堆栈跟踪的不同调用方数(通常每10个记录一次)

  • 为什么你认为`Thread.currentThread().getStackTrace()`更快?看一下[来源](http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/1ed30c084e3d/src/share/classes/java/lang/Thread.java#l1566):它是实现为`new Exception().getStackTrace()` (6认同)