java9系列(五)Stack-Walking API

codecraft

本文主要研究下JEP 259: Stack-Walking API

StackWalker

java9新增这个类的目的是提供一个标准API用于访问当前线程栈,之前只有Throwable::getStackTrace、Thread::getStackTrace以及SecurityManager::getClassContext提供了方法可以获取线程栈。

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;
    }
创建StackWalker的选项,可组合使用
  • RETAIN_CLASS_REFERENCE,可以使用StackWalker#getCallerClass(),StackFrame#getDeclaringClass(),StackFrame.getDeclaringClass()方法
  • SHOW_REFLECT_FRAMES,默认反射相关的frames是被隐藏的,使用这个选项可以开启
  • SHOW_HIDDEN_FRAMES,除了反射相关的frames外jvm还会隐藏其他一些实现相关的frame,使用这个选项可以将所有都输出

实例

使用Thread的getStackTrace

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

输出

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

输出

    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相关frames

输出所有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());
    }

输出

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)]

过滤指定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());
    }

输出

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

输出

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)]
这里skip了前两行

小结

java9引进的StackWalker可以更方便地打印当前线程堆栈,并提供了相关filter,skip等方法。

doc

阅读 1.4k

code-craft
spring boot , docker and so on 欢迎关注微信公众号: geek_luandun

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下了或重如泰山或轻如鸿毛的几笔。

11.3k 声望
1.1k 粉丝
0 条评论

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下了或重如泰山或轻如鸿毛的几笔。

11.3k 声望
1.1k 粉丝
宣传栏