从OpenJDK8到OpenJDK11 - StackWalker类

本文基于OpenJDK 11java

以前升级了JDK到OpenJDK11,把遇到的问题以及解决方案列一下。 每篇文章会以提出问题,思路说明,解决问题的思路去行文。 这篇文章是关于堆栈信息获取的。app

遇到的问题 - 调用堆栈获取

以前有作调用堆栈监控上报,某些仅采集调用类,某些须要采集调用方法,整体来讲:在Java8中,咱们能够这样去获取调用堆栈:工具

  1. 经过Reflection类:
private static void getCallStackClassNames() {
    StringBuffer sbStack = new StringBuffer();
    int i = 0;
    Class<?> caller = Reflection.getCallerClass(i++);
    do {
        sbStack.append(i + ".").append(caller.getName())
            .append("\n");
        caller = Reflection.getCallerClass(i++);
    } while (caller != null);
    LOGGER.info("{}", sbStack);
}

这种方式能够灵活地获取调用类,不用一会儿读取整个堆栈。可是缺点是:没法查看调用方法,信息不够详细 2. 经过Thread.currentThread().getStackTrace():代理

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

这种方法获取的信息很详细,可是一会儿返回整个堆栈的调用,不够方便。code

升级到OpenJDK11以后,sun.reflect.Reflection类没有了。get

思路说明

经过在Java 9以后JDK自带的工具jdeps来寻找可替代的类:it

jdeps  --jdk-internals ./target/AppName.jar

显示:io

...
JDK Internal API                         Suggested Replacement
----------------                         ---------------------
sun.reflect.Reflection                   Use java.lang.StackWalker @since 9

看到建议使用java.lang.StackWalker 咱们考虑用这个类替换sun.reflect.Reflection监控

解决问题

StackWalker能够灵活地查看每一帧调用。 初始化能够指定选项:jdk

//通常这样就足够用了,能够把每一个调用栈输出
StackWalker walker = StackWalker.getInstance();

也能够指定初始化参数:

//这样对于调用:StackWalker#getCallerClass()和StackFrame#getDeclaringClass()不会报异常,默认初始化是不支持这两个方法的
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

若是你想看反射的调用栈,例如Spring动态代理反射,能够这么初始化:

StackWalker walker = StackWalker.getInstance(StackWalker.Option.SHOW_REFLECT_FRAMES);

若是你想看完整的调用栈没有隐藏任何的调用栈,能够这么初始化:

StackWalker walker = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES);

以后应用,例如取第一个调用栈:

walker.walk(s -> s.limit(1).collect(Collectors.toList()));

将每个输出:

walker.forEach(System.out::println);
相关文章
相关标签/搜索