Monday, March 6, 2017

Deep Dive into Java 9’s Stack-Walking API

The stack-walking API, released as part of Java 9, offers an efficient way to access the execution stack. (The execution stack represents the chain of method calls - it starts with the public static void main(String[]) method or the run method of a thread, contains a frame for each method that was called but did not yet return, and ends at the execution point of the StackWalker call.) In this article we will explore the different functionalities of the stack-walking API, followed by a look at its performance characteristics.

This article requires working knowledge of Java, particularly lambda expressions and streams.

Who Called Me?

There are situations when you need to know who called your method. For example, to do security checks or to identify the source of a resource leak. Every method call creates a frame on the stack and Java allows code to access the stack, so it can analyze it.

Before Java 9, the way most people would access the stack information was via instantiating a Throwable and use it to get the stack trace.

StackTraceElement[] stackTrace = new Throwable().getStackTrace();

This works but it is quite costly and hacky. It captures all the frames - except the hidden ones - even if you need only the first 2 and does not give you access to the actual Class instance in which the method is declared. To get the class you need to extend SecurityManager that has a protected method getClassContext that will return an array of Class.

To address those drawbacks Java 9 introduces the new stack-walking API (with JEP 259). We will now explore the different functionalities of the API followed by a look at its performance characteristics.

StackWalker Basics

Java 9 ships with a new type, the StackWalker, which gives access to the stack. We will now see how to get an instance and how to use it to execute a simple stack walk.

Getting a StackWalker

A StackWalker is easily accessible with the static getInstance methods:

StackWalker stackWalker1 =
        StackWalker.getInstance();
StackWalker stackWalker2 =
        StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
StackWalker stackWalker3 =
        StackWalker.getInstance(
               Set.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));
StackWalker stackWalker4 =
        StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE), 32);

The different calls allow you to specify one option or a set of them as well as the estimated size of the number of frames to capture - I will discuss both further below.

Once you have your StackWalker you can access the stack information using the following methods.

The forEach Method

The forEach method will forward all the unfiltered frames to the specified Consumer<StackFrame> callback. So, for example, to just print the frames you do:

stackWalker.forEach(System.out::println);

Walk the walk

The walk method takes a function that gets a stream of stack frames and returns the desired result. It has the following signature (plus some wildcards that I removed to make it more readable):

<T> T walk(Function<Stream<StackWalker.StackFrame>, T> function)

You might ask why does it not just return the Stream? Let's come back to that later. First, we'll see how we can use it. For example, to collect the frames in a List you would write:

// collect the frames
List<StackWalker.StackFrame> frames = stackWalker.walk(
        frames -> frames.collect(Collectors.toList()));

To count them:

// count the number of frames
long nbFrames = stackWalker.walk(
        // the lambda returns a long
        frames -> frames.count());

One of the big advantages of using the walk method is that because the stack-walking API lazily evaluates frames, the use of the limit operator actually reduces the number of frames that are recovered. The following code will retrieve the first two frames, which, as we will see later, is much cheaper than capturing the full stack.

List<StackWalker.StackFrame> caller = stackWalker.walk(
        frames -> frames
                .limit(2)
                .collect(Collectors.toList()));

[caption id="attachment_149082" align="aligncenter" width="1024"]Concrete Stack Walker Published by Rory Hyde under CC-BY-SA 2.0 / SitePoint changed colorization and field of view and shares under the same license[/caption]

Advanced StackWalker

Continue reading %Deep Dive into Java 9’s Stack-Walking API%


by Arnaud Roger via SitePoint

No comments:

Post a Comment