How Expensive is Thread.getStackTrace()?

From what I recall, there's some impact in using Thread.getStackTrace() - especially with large stacks (such as when using in server-side or J2EE situations). You could try Throwable.getStackTrace() for better performance.

At any rate, calling those functions regularly (as opposed to doing so in an exception situation) will impact your app.


Yes, there is some overhead to this call, but in all likelyhood, you're going to do something like this:

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

then,

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

}

Which will cause you to not take the hit in your real code.

Even then, for exceptions, you're going to throw a whole stack traced Exception in your production build.

Note that if you are using a decent logging subsystem, they will probably already do something based on the logging level (in our log system, depending on the level, debug() is basically a no-op). Log4j and others have different ways of handling this.

Lastly, I'd say: Don't worry about it until it proves to be a real performance problem. Premature Optimization is the root of all evil :)


Now with JDK 9 & 10, you can use StackWalker, which is not an expensive call.

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());
            }
        });
    }

It looks like getting the current thread (and its associated ID) is not expensive, but getting the current thread and its stack trace is. The new throwable().getStackTrace() pattern seems to be a lot faster than the thread's stack trace pattern.

Also, note: this benchmark has almost no stack depth since its just a main method, so in a server environment this penalty will be a lot heavier.

Benchmark results:

Simple loop took 2 ms

Getting current thread took 10 ms

Getting stack trace took 29564 ms

Getting throwable stack trace took 19910 ms

Code:

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");

Tags:

Java

Logging