java9新特性-Stack Walking-当前线程栈信息
Posted Dreamer who
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java9新特性-Stack Walking-当前线程栈信息相关的知识,希望对你有一定的参考价值。
java语言是基于栈的设计语言,其执行的本质与c、c++语言一样,程序的运行都是一系列进栈出栈操作。JVM中的每个线程启动时都有一个私有的JVM线程栈会创建。栈这种数据结构就是我们常谈到的数据结构中的栈-后进先出的数据结构。栈保存了一系列栈帧,每当一个方法执行时都会伴随着新的栈帧的创建并进栈顶,方法执行完也都会伴随着对应的栈帧的销毁-出栈操作。有关具体细节可以参考https://docs.oracle.com/javase/specs/jvms/se8/html/index.html。
在异常日志中我们经常会看到类似:
其实打印的都是当前线程一系列栈中保存的代码信息,信息的输出的内容由栈顶到栈尾依次列出。
在java9之前我们一般这样获取栈信息:
public class TypeInference
public static void main(String[] args) throws IOException
test();
private static void test()
new Throwable().printStackTrace();
或者:
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
Stream.of(stackTrace).forEachOrdered(System.out::println);
public class TypeInference
public static void main(String[] args) throws IOException
test();
private static void test()
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Stream.of(stackTrace).forEachOrdered(System.out::println);
/** * Provides programmatic access to the stack trace information printed by * @link #printStackTrace(). Returns an array of stack trace elements, * each representing one stack frame. The zeroth element of the array * (assuming the array's length is non-zero) represents the top of the * stack, which is the last method invocation in the sequence. Typically, * this is the point at which this throwable was created and thrown. * The last element of the array (assuming the array's length is non-zero) * represents the bottom of the stack, which is the first method invocation * in the sequence. * * <p>Some virtual machines may, under some circumstances, omit one * or more stack frames from the stack trace. In the extreme case, * a virtual machine that has no stack trace information concerning * this throwable is permitted to return a zero-length array from this * method. Generally speaking, the array returned by this method will * contain one element for every frame that would be printed by * @code printStackTrace. Writes to the returned array do not * affect future calls to this method. * * @return an array of stack trace elements representing the stack trace * pertaining to this throwable. * @since 1.4 */ public StackTraceElement[] getStackTrace() return getOurStackTrace().clone();
根据java.lang.Throwable#getStackTrace文档:我们可以获取当前线程栈信息,数组的第一个元素为栈顶栈。
其实堆栈信息一般情况下我们不需要打印,异常堆栈信息由日志框架帮我们搞定。
但是在框架中,堆栈的获取还是有些价值的,比如spring boot框架中:
org.springframework.boot.SpringApplication#deduceMainApplicationClass
private Class<?> deduceMainApplicationClass()
try
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace)
if ("main".equals(stackTraceElement.getMethodName()))
return Class.forName(stackTraceElement.getClassName());
catch (ClassNotFoundException ex)
// Swallow and continue
return null;
上面代码利用栈信息寻找我们的main方法所在的spring boot启动类。
java9之前的这种获取栈信息有几个缺点:
- 效率低。java.lang.Throwable#getStackTrace会得到整个栈的信息快照。没办法控制只获取栈顶几个栈帧的信息。
- 栈帧包含的是方法的名字和类的名字,只是字符串形式,而非class<?>引用.
- JVM规范运行虚拟机实现的时候为了性能可以丢弃一些栈帧。
- 没办法针对类信息实现过滤
java9对这些缺点做了改进,引入了效率比较好的java.lang.StackWalker类,在获取栈帧的时候可以是懒汉式的。而且StackWalker是线程安全的。
StackWalker instance = StackWalker.getInstance();
instance.forEach(System.out::println);
或者:
StackWalker instance = StackWalker.getInstance();
instance.walk(s ->
s.forEach(System.out::println);
return null;
);
这种walk方法需要传递stream,而且是饿汉式操作的,我们可以过滤、获取栈个数等等流所支持的操作。
而且java.lang.StackWalker#getInstance(java.util.Set<java.lang.StackWalker.Option>, int)支持传入栈获取信息参数设置。
以上是关于java9新特性-Stack Walking-当前线程栈信息的主要内容,如果未能解决你的问题,请参考以下文章