本文基於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);