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

Java 访问性能良好的堆栈跟踪?,java,performance,stack-trace,Java,Performance,Stack Trace,我们最近应用了一种缓存解决方案,几乎所有应用程序模块/组件都使用该解决方案(约50个项目)。为了更好地了解在不同的系统“位置”上执行哪些缓存操作,我们为当前执行的缓存操作添加了日志记录,包括堆栈跟踪,以准确了解是什么触发了缓存操作 我们当前的方法是这样的:我们从new Throwable()获取堆栈跟踪,过滤不相关的行并记录剩余的堆栈跟踪。遗憾的是,创建一个新的异常来记录日志并不是一个便宜的操作。因为我们不直接使用缓存,而是通过hibernate,所以在没有访问堆栈跟踪的情况下很难找出是哪个调用

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


我们当前的方法是这样的:我们从new Throwable()获取堆栈跟踪,过滤不相关的行并记录剩余的堆栈跟踪。遗憾的是,创建一个新的异常来记录日志并不是一个便宜的操作。因为我们不直接使用缓存,而是通过hibernate,所以在没有访问堆栈跟踪的情况下很难找出是哪个调用方触发了操作

因此,我的问题是:有没有比Throwable().getStackTrace或Thread.currentThread().getStackTrace()更有效的方法来访问当前stacktrace

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

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


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

您还可以实现一个新的方法。这将正确插入要跟踪的代码片段


实际上获取异常堆栈跟踪的速度并不慢:

  • 只有10%时间花费在收集回溯上-这是在
    Throwable()
    构造函数中完成的
  • 其他90%将回溯从内部格式转换为可读的
    StackTraceeElement[]
    数组所花费的时间
    getStackTrace()
    中完成
这意味着,如果不需要同步处理堆栈跟踪,可以只调用
newexception()
(这或多或少是一个快速操作),然后稍后或在另一个线程中异步调用
e.getStackTrace()

此外,有时(如您的情况)不需要完整的堆栈跟踪。您可以跳过一些堆栈帧,只解码您感兴趣的帧。magic
sun.misc.sharedSectes
类会有所帮助

例如,要仅获取第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);
}
Exception e=newexception();
int depth=Math.min(5,SharedSecrets.getJavaLangAccess().getStackTraceDepth(e));
对于(int-frame=2;frame
“遗憾的是,创建一个新的异常来记录日志并不是一个便宜的操作”——实际上,我想不出还有比这更昂贵的操作了。你不能从它的源代码中安装hibernate吗?是的,我想我们可以。我们也讨论了这一点,但我们想先用这个不需要动脑筋的解决方案来尝试一下。Thx,对于次采样建议!没有想到。为什么你认为
Thread.currentThread().getStackTrace()
更快?看看:如果可以的话,它被实现为
newexception().getStackTrace()
@apangin+10。谢谢。如何避免需要进行堆栈跟踪?其想法是,使用工具界面,他可以使用方法输入事件来获取帧位置并遍历堆栈。值得注意的是,sun.*包用于内部使用,不应由“最终”用户使用。imho说,这意味着,在创建新的java版本时,与官方公开的包相比,在保持sun.*包向后兼容方面要小心得多。@reallyce是的
SharedSecrets
适用于Java 8。它在Java9中不起作用,但是,Java9和更高版本有了新的标准。