本文基于OpenJDK 11java
以前升级了JDK到OpenJDK11,把遇到的问题以及解决方案列一下。 每篇文章会以提出问题,思路说明,解决问题的思路去行文。 这篇文章是关于堆栈信息获取的。app
以前有作调用堆栈监控上报,某些仅采集调用类,某些须要采集调用方法,整体来讲:在Java8中,咱们能够这样去获取调用堆栈:工具
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);