Stack-Walking API – Java 9

A stack trace is a list of method calls arranged in such a way that the method at which an exception was thrown will be at the top followed by the caller to that method and so on till the main method of the program. We can print a stack trace to the console by invoking printStacktrace() on the exception caught in the catch block of a try-catch statement.

While developing Java-based applications, sometimes we would want to traverse the execution stack to analyze what is going on at runtime. This would be required in situations where we need to trace why there is a memory leak or excessive CPU consumption, why a particular exception was thrown during runtime in the application logs, etc.

Getting Current Execution Stack Before Java 9

Prior to Java 9, if someone wanted to get this information, he/she would invoke the getStackTrace() on a Throwable instance. Let us see how it was done with an example:

Output for the above code will be as follows:

In the above example, we saw how to use getStackTrace() for getting class name, method name, and line number. But if we want to get the Class objects of each class in the execution stack, we will have to create a subclass of java.lang.SecurityManager and invoke the protected method getClassContext() from that subclass.

There are a few problems while following this approach:

  • First of all, it impacts performance and will not contain all details that we want because the snapshot thus produced is that of the entire stack, but will not contain hidden frames.
  • We need to examine all the frames in the process, which is, again a costly approach.
  • The specification for getStackTrace() method added to java.lang.Thread and java.lang.Throwable allows the JVM implementation to return partial information for the method call. So getStackTrace() method may return partial stack information as a result of optimization of getStackTrace() in the JVM implementation. This partial stack information may not be desirable in situations where we need the full stack information.

The Stack-Walking API

Java 9 (JEP 259) introduced java.lang.StackWalker class as an alternative to SecurityManager.getClassContext() and Thread.getStackTrace() or Throwable.getStackTrace(). The StackWalker class addresses most of the problems faced while using the old methods as discussed earlier. Unlike getStackTrace() which will be giving StackTraceElement array per thread, a single StackWalker can be used by multiple threads for traversing their own stack as it is thread-safe.

There are a nested enum and a nested interface available inside StackWalker class:

  • StackWalker.StackFrame – This is the nested interface inside StackWalker class. The obtained object represents a method invocation by StackWalker. From this StackFrame object, it is possible to get information like the file, class, method name and even line number and the corresponding StackTraceElement object for the particular stack frame.
  • StackWalker.Option – This is the enum available that denotes which all information of the stack shall be accessed. This enum value is used to get a customized StackWalker object which will be discussed in next subsection.

Getting A StackWalker Instance

There are four overloaded getInstance() methods of StackWalker that can be used to get a StackWalker object:

  • getInstance() – returns a StackWalker instance.
  • getInstance( StackWalker.Option option ) – returns a StackWalker instance which contains the stack frame information according to the specified enum value.
  • getInstance( Set[StackWalker.Option] optionsSet ) – returns a StackWalker instance which contains the stack frame information according to the set of specified Option enum values. In case the optionSet is empty, the returned StackWalker will not contain hidden frames or class references.
  • getInstance( Set[StackWalker.Option] optionsSet, int estimateDepth ) – similar to the previous method mentioned. This returns a StackWalker instance which contains stack frame information according to the optionSet values. The estimateDepth value is used to tell how many numbers of stack frames the instance shall traverse. If the estimateDepth is less than or equal to zero, this method will throw an IllegalArgumentException.

In the above class, testWalker() is called from method3(). So, there will not be any stack frame corresponding to method4() in the StackWalker instance. The method4() will throw a FileNotFoundException as the file mentioned in PATH is not created yet. Note that we have created two instances of StackWalker and printed them. This is to show how RETAIN_CLASS_REFERENCE Option is used.

Output for the above will be as follows:

If we uncomment the last two lines, it will throw UnsupportedOperationException as the object w1 was obtained without explicitly specifying RETAIN_CLASS_REFERENCE option. The output will look like:

Using StackWalker.StackFrame

As discussed in the section ‘The StackWalker API,’ the nested interface StackFrame can be used to get more information about the execution stack.

In the above code we use methods from each StackFrame object to get more information about that frame. Output for the above code will be as follows:

Note that in the above program, if the StackWalker is not created using RETAIN_CLASS_REFERENCE option, it will throw an UnsupportedOperationException while invoking getDeclaringClass().

Partial Traversal Of Stack Using walk() Method

In a large project, if we are invoking the StackWalker, there will be many frames in the execution stack. As developers, we may want to traverse only a part of the stack instead of the full stack. It is possible to limit how many StackFrames are inspected using the walk() method. Using filter(), skip(), limit() etc, we have multiple ways to filter the stack frames according to our need.

Output of the above code will be as follows:

Summary

In this article, we have discussed the new Stack-Walking API introduced in Java 9. It effectively helps us to get a snapshot of the execution stack efficiently. Class references and hidden frames are excluded by default when you get an instance of StackWalker using StackWalker.getInstance(). The API was designed in this way to make sure that there is no loss in performance while traversing the execution stack. In addition to this, further enhancement in performance can be done by adding certain criterion as a lambda expression parameter to walk() method, as discussed in the last section. The StackWalker API shows better performance than getStackTrace() and SecurityManager.getClassContext().

2 thoughts to “Stack-Walking API – Java 9”

  1. I really like the way java 8 has made it easy to print lists. framesList.forEach(System.out::println);. Great write up. Thanks.

Leave a Reply