Java9 series (5) Stack-Walking API

  java9

Order

This article mainly studiesJEP 259: Stack-Walking API

StackWalker

Java9 added this class to provide a standard API for accessing the current thread stack. Previously, only Throwable::getStackTrace, Thread::getStackTrace and SecurityManager:: GetClassContext provided methods to obtain the thread stack.

StackWalker.Option

/Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home/lib/src.zip! /java.base/java/lang/StackWalker.java

    /**
     * Stack walker option to configure the {@linkplain StackFrame stack frame}
     * information obtained by a {@code StackWalker}.
     *
     * @since 9
     */
    public enum Option {
        /**
         * Retains {@code Class} object in {@code StackFrame}s
         * walked by this {@code StackWalker}.
         *
         * <p> A {@code StackWalker} configured with this option will support
         * {@link StackWalker#getCallerClass()} and
         * {@link StackFrame#getDeclaringClass() StackFrame.getDeclaringClass()}.
         */
        RETAIN_CLASS_REFERENCE,
        /**
         * Shows all reflection frames.
         *
         * <p>By default, reflection frames are hidden.  A {@code StackWalker}
         * configured with this {@code SHOW_REFLECT_FRAMES} option
         * will show all reflection frames that
         * include {@link java.lang.reflect.Method#invoke} and
         * {@link java.lang.reflect.Constructor#newInstance(Object...)}
         * and their reflection implementation classes.
         *
         * <p>The {@link #SHOW_HIDDEN_FRAMES} option can also be used to show all
         * reflection frames and it will also show other hidden frames that
         * are implementation-specific.
         *
         * @apiNote
         * This option includes the stack frames representing the invocation of
         * {@code Method} and {@code Constructor}.  Any utility methods that
         * are equivalent to calling {@code Method.invoke} or
         * {@code Constructor.newInstance} such as {@code Class.newInstance}
         * are not filtered or controlled by any stack walking option.
         */
        SHOW_REFLECT_FRAMES,
        /**
         * Shows all hidden frames.
         *
         * <p>A Java Virtual Machine implementation may hide implementation
         * specific frames in addition to {@linkplain #SHOW_REFLECT_FRAMES
         * reflection frames}. A {@code StackWalker} with this {@code SHOW_HIDDEN_FRAMES}
         * option will show all hidden frames (including reflection frames).
         */
        SHOW_HIDDEN_FRAMES;
    }

The option to create StackWalker can be used in combination.

  • RETAIN_CLASS_REFERENCE, StackWalker#getCallerClass (), StackFrame#getDeclaringClass (), StackFrame.getDeclaringClass () method can be used
  • SHOW_REFLECT_FRAMES, the default reflection-related frames are hidden. Use this option to turn on
  • SHOW_HIDDEN_FRAMES, jvm will hide some other implementation-related frames besides reflection-related frames. Use this option to output all of them.

Example

GetStackTrace using Thread

    @Test
    public void testPrintCurrnentStackTrace(){
        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : stack){
            System.out.println(element);
        }
    }

Output

java.base/java.lang.Thread.getStackTrace(Thread.java:1654)
test.com.packt.lang.StackWalkingTest.testPrintCurrnentStackTrace(StackWalkingTest.java:20)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:564)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
org.junit.runners.ParentRunner.run(ParentRunner.java:363)
org.junit.runner.JUnitCore.run(JUnitCore.java:137)
com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

SHOW_REFLECT_FRAMES

    private void print(StackWalker stackWalker){
        stackWalker.forEach(stackFrame -> System.out.printf("%6d| %s -> %s %n",
                stackFrame.getLineNumber(),
                stackFrame.getClassName(),
                stackFrame.getMethodName()));
    }

    @Test
    public void testShowReflectFrames(){
        final StackWalker stackWalker =
                StackWalker.getInstance(Option.SHOW_REFLECT_FRAMES);
        print(stackWalker);
    }

Output

    19| test.com.packt.lang.StackWalkingTest -> print 
    43| test.com.packt.lang.StackWalkingTest -> testShowReflectFrames 
    -2| jdk.internal.reflect.NativeMethodAccessorImpl -> invoke0 
    62| jdk.internal.reflect.NativeMethodAccessorImpl -> invoke 
    43| jdk.internal.reflect.DelegatingMethodAccessorImpl -> invoke 
   564| java.lang.reflect.Method -> invoke 
    50| org.junit.runners.model.FrameworkMethod$1 -> runReflectiveCall 
    12| org.junit.internal.runners.model.ReflectiveCallable -> run 
    47| org.junit.runners.model.FrameworkMethod -> invokeExplosively 
    17| org.junit.internal.runners.statements.InvokeMethod -> evaluate 
   325| org.junit.runners.ParentRunner -> runLeaf 
    78| org.junit.runners.BlockJUnit4ClassRunner -> runChild 
    57| org.junit.runners.BlockJUnit4ClassRunner -> runChild 
   290| org.junit.runners.ParentRunner$3 -> run 
    71| org.junit.runners.ParentRunner$1 -> schedule 
   288| org.junit.runners.ParentRunner -> runChildren 
    58| org.junit.runners.ParentRunner -> access$000 
   268| org.junit.runners.ParentRunner$2 -> evaluate 
   363| org.junit.runners.ParentRunner -> run 
   137| org.junit.runner.JUnitCore -> run 
    68| com.intellij.junit4.JUnit4IdeaTestRunner -> startRunnerWithArgs 
    47| com.intellij.rt.execution.junit.IdeaTestRunner$Repeater -> startRunnerWithArgs 
   242| com.intellij.rt.execution.junit.JUnitStarter -> prepareStreamsAndStart 
    70| com.intellij.rt.execution.junit.JUnitStarter -> main

Jdk.internal.reflect Related frames Output More Than Default

Output all frames

    @Test
    public void testGetAllFrames(){
        List<StackWalker.StackFrame> stack =
                StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((s) -> s.collect(Collectors.toList()));
        System.out.println("All frames : \n" + stack.toString());
    }

Output

All frames : 
[test.com.packt.lang.StackWalkingTest.testGetAll(StackWalkingTest.java:54), org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50), org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12), org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47), org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17), org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325), org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78), org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57), org.junit.runners.ParentRunner$3.run(ParentRunner.java:290), org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71), org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288), org.junit.runners.ParentRunner.access$000(ParentRunner.java:58), org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268), org.junit.runners.ParentRunner.run(ParentRunner.java:363), org.junit.runner.JUnitCore.run(JUnitCore.java:137), com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68), com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47), com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242), com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)]

Filter specified class

    @Test
    public void testFilterByClass(){
        Optional<StackWalker.StackFrame> filteredResult = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((s) ->
                s.filter(f -> StackWalkingTest.class.equals(f.getDeclaringClass())).findFirst()
        );
        System.out.println("filter by class : \n"+filteredResult.toString());
    }

Output

filter by class : 
Optional[test.com.packt.lang.StackWalkingTest.testFilterByClass(StackWalkingTest.java:62)]

skip

    @Test
    public void testSkip(){
        List<StackWalker.StackFrame> framesAfterSkip = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((s) ->
                s.skip(2).collect(Collectors.toList()));
        System.out.println("frames after skip : \n"+framesAfterSkip.toString());
    }

Output

frames after skip : 
[org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12), org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47), org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17), org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325), org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78), org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57), org.junit.runners.ParentRunner$3.run(ParentRunner.java:290), org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71), org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288), org.junit.runners.ParentRunner.access$000(ParentRunner.java:58), org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268), org.junit.runners.ParentRunner.run(ParentRunner.java:363), org.junit.runner.JUnitCore.run(JUnitCore.java:137), com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68), com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47), com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242), com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)]

Here skip the first two lines

Summary

StackWalker introduced by java9 can print the current thread stack more conveniently, and provides methods such as filter and skip.

doc