美文网首页
聊聊如何在应用代码里捕获线程堆栈

聊聊如何在应用代码里捕获线程堆栈

作者: go4it | 来源:发表于2023-12-19 09:24 被阅读0次

    本文主要研究一下如何在应用代码里捕获线程堆栈

    getRunnableStackTraces

    org/h2/util/Profiler.java

        private static List<Object[]> getRunnableStackTraces() {
            ArrayList<Object[]> list = new ArrayList<>();
            Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
            for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
                Thread t = entry.getKey();
                if (t.getState() != Thread.State.RUNNABLE) {
                    continue;
                }
                StackTraceElement[] dump = entry.getValue();
                if (dump == null || dump.length == 0) {
                    continue;
                }
                list.add(dump);
            }
            return list;
        }
    

    h2的Profiler的getRunnableStackTraces方法通过Thread.getAllStackTraces()来收集线程堆栈

    readRunnableStackTraces

    org/h2/util/Profiler.java

        private static List<Object[]> readRunnableStackTraces(int pid) {
            try {
                String jstack = exec("jstack", Integer.toString(pid));
                LineNumberReader r = new LineNumberReader(
                        new StringReader(jstack));
                return readStackTrace(r);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        private static String exec(String... args) {
            ByteArrayOutputStream err = new ByteArrayOutputStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            try {
                Process p = Runtime.getRuntime().exec(args);
                copyInThread(p.getInputStream(), out);
                copyInThread(p.getErrorStream(), err);
                p.waitFor();
                String e = new String(err.toByteArray(), StandardCharsets.UTF_8);
                if (e.length() > 0) {
                    throw new RuntimeException(e);
                }
                return new String(out.toByteArray(), StandardCharsets.UTF_8);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }    
    

    h2的Profiler的readRunnableStackTraces方法则是基于给定的pid使用jstack来捕获线程堆栈

    CommandProcessor

    sun/jvm/hotspot/CommandProcessor.java

    new Command("jstack", "jstack [-v]", false) {
                public void doit(Tokens t) {
                    boolean verbose = false;
                    if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
                        verbose = true;
                    }
                    StackTrace jstack = new StackTrace(verbose, true);
                    jstack.run(out);
                }
            }
    

    sun.jvm.hotspot包的CommandProcessor提供了对jstack的支持

    ThreadDumpEndpoint

    org/springframework/boot/actuate/management/ThreadDumpEndpoint.java

        @ReadOperation(produces = "text/plain;charset=UTF-8")
        public String textThreadDump() {
            return getFormattedThreadDump(this.plainTextFormatter::format);
        }
    
        private <T> T getFormattedThreadDump(Function<ThreadInfo[], T> formatter) {
            return formatter.apply(ManagementFactory.getThreadMXBean().dumpAllThreads(true, true));
        }   
    

    springboot的ThreadDumpEndpoint则使用的是ManagementFactory.getThreadMXBean().dumpAllThreads来获取线程堆栈

    小结

    在java运行时可以通过Thread.getAllStackTraces()、ManagementFactory.getThreadMXBean().dumpAllThreads来获取当前进程的线程堆栈信息,也可以通过Process调用jstack命令,值得注意的是jstack捕获的线程堆栈包含了nid(比如"C2 CompilerThread0" #7 daemon prio=9 os_prio=31 cpu=481.27ms elapsed=36.74s tid=0x00007fb08c068400 nid=0x6803 waiting on condition [0x0000000000000000]),也就是top -H -p pid中展示的PID信息,而前面两个方法dump出来的没有nid这个信息。

    相关文章

      网友评论

          本文标题:聊聊如何在应用代码里捕获线程堆栈

          本文链接:https://www.haomeiwen.com/subject/csjzgdtx.html