序
本文主要研究下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}.
*
* 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.
*
*
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.
*
*
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.
*
*
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 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 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 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
- JDK 9 features
- StackWalker
- JEP 259: Stack-Walking API
- Introduction to Java 9 StackWalking API
- Java 9 - StackWalker API
- JDK 9: An Introduction to StackWalker
- Java 9 Will Change the Way You Traverse Stack Traces