我们最近应用了几乎所有应用程序模块/组件(大约50个项目)使用的缓存解决方案。为了更好地了解在不同的系统“位置”上执行哪些缓存操作,我们为当前执行的缓存操作添加了日志记录,包括堆栈跟踪,以准确了解触发缓存操作的内容。
我们当前的方法如下所示:我们从新的Throwable()中获取堆栈跟踪,过滤不相关的行并记录剩余的堆栈跟踪。尽管如此,创建一个新的异常以便记录是不便宜的。由于我们不直接使用缓存,而是通过休眠,因此找不到哪个调用者在没有访问堆栈跟踪的情况下触发了操作就不那么容易了。
我的问题是:是否有一个更高效的解决方案来访问当前的堆栈跟踪然后Throwable()。getStackTrace或Thread.currentThread()。getStackTrace()?
答案 0 :(得分:13)
实际上获取异常堆栈跟踪并不是那么慢:
Throwable()
构造函数中完成的; StackTraceElement[]
数组的回溯 - 这是在getStackTrace()
中完成的。 这意味着,如果您不需要同步处理堆栈跟踪,则只需调用new Exception()
(这或多或少是快速操作),然后稍后调用e.getStackTrace()
或在另一个线程中异步。
此外,有时(就像你的情况一样)不需要完整的堆栈跟踪。您可以跳过一些堆栈帧并仅解码您感兴趣的那些。魔术sun.misc.SharedSectets
类将有所帮助。
E.g。要获得第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);
}
答案 1 :(得分:6)
Thread.currentThread().getStackTrace()
与@apangin指出的基本相同,但如果它避免创建Throwable,它将来会更快。
但是,您可能会发现子采样将为您提供所需的改进。而不是记录每次访问,记录每第N次访问。如果你记录每10次访问,你可以减少高达90%的开销,如果你有大量的访问,它几乎一样准确。
另一种选择是使用像YourKit这样的分析器,它可以更有效地完成这项工作。这可以向您显示方法及其堆栈跟踪的不同调用方数(通常每10个记录一次)
答案 2 :(得分:0)
您还可以执行java agent。这将正确地检测您想要跟踪的代码片段。