10

We recently applied a caching solution that is used by almost all application modules/components (approx. 50 projects). To get a better understanding which cache operations are executed on the different system "places", we added logging for the currently performed cache operations including a stack trace to exactly know what triggered the cache operation.

Our current approach looks like this: We take the stack trace from new Throwable(), filter the irrelevant lines and log the remaining stack trace. Creating a new exception to log regretfully is no cheap operation though. Since we do not use the cache directly but through hibernate it's not so easy to find out which caller triggered the operation without having access to the stack trace.

My question thus is: Is there a more performant solution to access the current stacktrace then Throwable().getStackTrace or Thread.currentThread().getStackTrace()?

u6f6o
  • 2,050
  • 3
  • 29
  • 54
  • 3
    "Creating a new exception to log regretfully is no cheap operation though" -- actually I can't think on any other operation that can be more expensive than this. Can't you instrument hibernate from its source code? – Leo Sep 30 '14 at 08:46
  • Yes, I think we can. We also discussed this but we wanted to try it with this no-brainer solution first. – u6f6o Sep 30 '14 at 08:55

3 Answers3

18

Actually getting an exception stack trace is not such slow:

  • only 10% time is spent collecting the backtrace - this is done in Throwable() constructor;
  • other 90% time is spent converting the backtrace from the internal format to a readable StackTraceElement[] array - this is done in getStackTrace().

This means, if you don't need to process the stack trace synchronously, you can just call new Exception() (this is more or less fast operation), and then invoke e.getStackTrace() later or asynchronously in another thread.

Moreover, sometimes (like in your situation) the full stack trace is not needed. You may skip some stack frames and decode only those you are interested in. The magic sun.misc.SharedSectets class will help.

E.g. to get only frames 2 to 5, use

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);
}
apangin
  • 92,924
  • 10
  • 193
  • 247
  • It's worth being aware that sun.* packages are meant for internal uses, and shouldn't be used by "final" users. Among the other things this means, imho, that when creating a new java version, there's much less care in keeping sun.* packages backwards compatible than for officially exposed packages. – reallynice Oct 12 '16 at 10:45
  • 6
    @reallynice Right. `SharedSecrets` works up to Java 8. It won't work in Java 9, however, there is new standard [StackWalker API](http://download.java.net/java/jdk9/docs/api/java/lang/StackWalker.html) for Java 9 and beyond. – apangin Oct 12 '16 at 11:50
5

Thread.currentThread().getStackTrace() is basically the same thing as @apangin points out, but it could be faster in future if it avoided creating a Throwable.

However, you might find sub-sampling will give you the improvement you need. Instead of recording every access, record every N-th access. If you record every 10th access you can reduce the overhead by up to 90% and if you have a high number of accesses it will be almost as accurate.

Another option is to use a profiler like YourKit which can do this much more efficiently. This can show you how much different callers to a method and their stack traces (typically it records every 10th)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Thx, for the sub-sampling suggestion! Didn't think of that. – u6f6o Sep 30 '14 at 09:02
  • 9
    Why do you think `Thread.currentThread().getStackTrace()` is faster? Have a look at the [sources](http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/1ed30c084e3d/src/share/classes/java/lang/Thread.java#l1566): it is implemented as `new Exception().getStackTrace()` – apangin Sep 30 '14 at 10:39
0

What more you can do is to implement a java agent. That will instrument properly the code fragment you want to trace.

Here you will find how to start

Community
  • 1
  • 1