Thread.getStackTrace()有多昂贵?

Jai*_*cia 17 java logging

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

Kyl*_*lar 8

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

public static boolean DEBUG_ON = true; //change this before your production build
Run Code Online (Sandbox Code Playgroud)

然后,

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 在任何给定时间,我的(Windows)系统运行大约50-80个程序,服务和设备驱动程序.如果他们每个人只使用的资源(CPU和内存)比程序员只考虑一点性能要多20%,那么这相当于我的硬件可以做的事情.在**编写代码**之前考虑性能**并不是所有邪恶的根源(对金钱的热爱). (6认同)
  • 该条件可以使用 logger.isDebugEnabled() 代替布尔标志 (3认同)

Ily*_*lya 8

看起来获取当前线程(及其关联的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");
Run Code Online (Sandbox Code Playgroud)

  • "让堆栈跟踪花费29564毫秒"是一个荒谬的说法; 测试代码表明需要2,900**纳米*秒.另外@Roman对于优化更改结果完全正确. (7认同)
  • 这个基准打破了.javac/JVM将优化它到第1和第2个循环被完全删除的程度.此外,毫不能在这里使用millis. (4认同)

use*_*260 6

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

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());
            }
        });
    }
Run Code Online (Sandbox Code Playgroud)


Tra*_*Guy 5

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

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

  • @SoftwareMonkey 实际上确实如此: Throwable.fillInStackTrace() 可以利用知道它正在检查调用该方法的同一线程的堆栈的优势,而 Thread.getStackTrace() 必须是线程安全的,而且成本要高得多。参见 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302 (8认同)