Thread.getStackTrace()有多昂贵?

在日志记录系统中,每个日志输出都由辅助类完成,并使用此方法

public void debug(String message) { Logger logger = Logger.getLogger(getCallingClass()); logger.debug(message); } ... public Class getCallingClass() { /* Calls Thread.getStackTrace() and back traces until the class on the stack trace != this.getClass(). */ return classFound; } 

它的运行成本有多高,是否会有显着的性能提升?

是的,这个电话有一些开销,但在所有可能的情况下,你会做这样的事情:

 public static boolean DEBUG_ON = true; //change this before your production build 

然后,

 public void debug(String message){ if(DEBUG_ON){ //stack code here } } 

这将导致您不接受真实代码中的命中。

即便如此,对于exception,您将在生成构建中抛出整个堆栈跟踪的Exception。

请注意,如果您正在使用一个不错的日志记录子系统,他们可能已经根据日志记录级别做了一些事情(在我们的日志系统中,取决于级别,debug()基本上是无操作)。 Log4j和其他人有不同的处理方式。

最后,我要说:不要担心它,直到它被certificate是一个真正的性能问题。 过早优化是万恶之源:)

看起来获取当前线程(及其关联的ID)并不昂贵,但获取当前线程及其堆栈跟踪是。 新的throwable()。getStackTrace()模式似乎比线程的堆栈跟踪模式快得多。

另外,请注意:此基准测试几乎没有堆栈深度,因为它只是一个主要方法,因此在服务器环境中,这种惩罚将会更加沉重。

基准测试结果:

简单循环耗时2毫秒

获取当前线程需要10毫秒

获得堆栈跟踪需要29564毫秒

获得可抛弃的堆栈跟踪需要19910毫秒

码:

 int trials = 10_000_000; long start = System.currentTimeMillis(); long a = 1; for (int i = 0; i < trials; i += 1) { a += 1; } long duration = System.currentTimeMillis() - start; System.out.println("Simple loop took " + duration + " ms"); start = System.currentTimeMillis(); a = 1; for (int i = 0; i < trials; i += 1) { a += 1; Thread.currentThread().getId(); } duration = System.currentTimeMillis() - start; System.out.println("Getting current thread took " + duration + " ms"); start = System.currentTimeMillis(); a = 1; for (int i = 0; i < trials; i += 1) { a += 1; Thread.currentThread().getStackTrace(); } duration = System.currentTimeMillis() - start; System.out.println("Getting stack trace took " + duration + " ms"); start = System.currentTimeMillis(); a = 1; for (int i = 0; i < trials; i += 1) { a += 1; (new Throwable()).getStackTrace(); } duration = System.currentTimeMillis() - start; System.out.println("Getting throwable stack trace took " + duration + " ms"); 

根据我的记忆,使用Thread.getStackTrace()产生一些影响 – 尤其是对于大型堆栈(例如在服务器端或J2EE情况下使用时)。 您可以尝试Throwable.getStackTrace()以获得更好的性能。

无论如何,定期调用这些函数(而不是在exception情况下这样做)将影响您的应用程序。

现在使用JDK 9和10,您可以使用StalkWalker,这不是一个昂贵的调用。

 private void invoke006() { var stack = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk((s) -> s.collect(Collectors.toList())); stack.forEach(stackFrame -> { if (stackFrame.getMethodName().equals("masterInvoker")) { System.err.println("master called !!"); System.err.println(StackWalker.getInstance().walk((s) -> s.collect(Collectors.toList())).get(0).getMethodName() + ", line: " + StackWalker.getInstance().walk((s) -> s.collect(Collectors.toList())).get(0).getLineNumber()); } }); }