[alibaba/arthas]stack命令可能会在 StackAdviceListener 抛出 NullPointerException

2025-11-10 237 views
5
2021-01-21 18:54:39 [http-nio-7001-exec-33] WARN  c.t.a.c.c.m.StackAdviceListener -stack failed.
java.lang.NullPointerException: null
  at com.taobao.arthas.core.command.monitor200.StackAdviceListener.finishing(StackAdviceListener.java:67)
  at com.taobao.arthas.core.command.monitor200.StackAdviceListener.afterReturning(StackAdviceListener.java:52)
  at com.taobao.arthas.core.advisor.AdviceListenerAdapter.afterReturning(AdviceListenerAdapter.java:57)
  at com.taobao.arthas.core.advisor.SpyImpl.atExit(SpyImpl.java:67)
  at java.arthas.SpyAPI.atExit(SpyAPI.java:64)

原因是 com.taobao.arthas.core.command.monitor200.StackAdviceListener#stackThreadLocal

    private final ThreadLocal<StackModel> stackThreadLocal = new ThreadLocal<StackModel>();

    private void finishing(Advice advice) {
        // 本次调用的耗时
        try {
            double cost = threadLocalWatch.costInMillis();
            boolean conditionResult = isConditionMet(command.getConditionExpress(), advice, cost);
            if (this.isVerbose()) {
                process.write("Condition express: " + command.getConditionExpress() + " , result: " + conditionResult + "\n");
            }
            if (conditionResult) {
                // TODO: concurrency issues for process.write
                // TODO: should clear stackThreadLocal?
                StackModel stackModel = stackThreadLocal.get();
                stackModel.setTs(new Date());
                process.appendResult(stackModel);
                process.times().incrementAndGet();
                if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) {
                    abortProcess(process, command.getNumberOfLimit());
                }
            }
stackThreadLocal 没有正确初始化,参考ThreadLocal的javadoc stackThreadLocal里的对象被放到异步queue里了,会有并发问题

回答

7

本质原因是 stack 命令增强之后,jvm并不会严格保证 StackAdviceListener#before 调用之后,再调用 StackAdviceListener#finishing

所以导致 stackThreadLocal.get() 可能返回null。

实际上 ThreadLocal<StackModel> stackThreadLocal ,并不是必要的,直接在 StackAdviceListener#finishing取出 stack信息即可。

可能有影响的是 traceid在 before 到 finishing 调用之间会变化。其它的无影响。

3

本质原因是 stack 命令增强之后,jvm并不会严格保证 StackAdviceListener#before 调用之后,再调用 StackAdviceListener#finishing

所以导致 stackThreadLocal.get() 可能返回null。

实际上 ThreadLocal<StackModel> stackThreadLocal ,并不是必要的,直接在 StackAdviceListener#finishing取出 stack信息即可。

可能有影响的是 traceid在 before 到 finishing 调用之间会变化。其它的无影响。

准确来说,不是JVM的问题。是因为arthas改为只增强一次字节码了,所以会出现新增加 listener id时会出现下面的情况:

method enter 插入了新的 listener id 调用到 StackAdviceListener#finishing method exit

所以才会出现issue里的问题。