[alibaba/arthas]当ShutDown关机hooks第0个slot被占用后,无法启动arthas

2024-02-23 43 views
7
环境信息
  • arthas-boot.jar 或者 as.sh 的版本: 3.3.7
  • Arthas 版本: 3.37
  • 操作系统版本: win10
  • 目标进程的JVM版本: 1.8
  • 执行arthas-boot的版本: 3.3.7
重现问题的步骤

1.添加关机钩子时占用了ShutDown hooks 数组里边的第0个slot

  1. 启动arthas-boot
  2. 选中需要attach进程 下边是我们添加关机钩子的代码 基于spring-boot应用
public class WebApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(WebApplication.class, args);
        ShutdownHookAssembler.addShutdownHooks(new Thread());
    }
}
class ShutdownHookAssembler {
    public ShutdownHookAssembler() {
    }

    public static void addShutdownHooks(Runnable shutdownHook) {
        try {
            Class<?> clazz = Class.forName("java.lang.Shutdown");
            Field field = clazz.getDeclaredField("hooks");
            field.setAccessible(true);
            synchronized(clazz) {
                Runnable[] hooks = (Runnable[])((Runnable[])field.get(clazz));
                int firstEmptySlot = findFirstEmptySlot(hooks);
                if (firstEmptySlot == 10) {
                    System.out.println("关机钩子满了!");
                    return;
                }

                if (firstEmptySlot > 0) {
                    for(int i = firstEmptySlot; i > 0; --i) {
                        hooks[i] = hooks[i - 1];
                    }
                }

                hooks[0] = shutdownHook;
                System.out.println("已添加关机钩子!");
            }
        } catch (Exception e) {
            System.out.println("error! " + e.getMessage());
        }

    }

    private static int findFirstEmptySlot(Runnable[] hooks) {
        for(int i = 0; i < 10; ++i) {
            if (hooks[i] == null) {
                return i;
            }
        }

        return 10;
    }
}
期望的结果

arthas正常启动并且attach到目标进程

实际运行的结果

启动失败

Arthas server agent start...
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.taobao.arthas.agent334.AgentBootstrap.bind(AgentBootstrap.java:182)
    at com.taobao.arthas.agent334.AgentBootstrap.access$000(AgentBootstrap.java:20)
    at com.taobao.arthas.agent334.AgentBootstrap$1.run(AgentBootstrap.java:152)
Caused by: java.lang.InternalError: Shutdown hook at slot 0 already registered
    at java.lang.Shutdown.add(Shutdown.java:97)
    at java.lang.System$2.registerShutdownHook(System.java:1255)
    at java.io.Console.<clinit>(Console.java:522)
    at sun.misc.Unsafe.ensureClassInitialized(Native Method)
    at sun.misc.SharedSecrets.getJavaIOAccess(SharedSecrets.java:128)
    at java.lang.System.console(System.java:215)
    at com.taobao.arthas.common.AnsiLog.<clinit>(AnsiLog.java:56)
    at com.taobao.arthas.core.util.LogUtil.initLooger(LogUtil.java:59)
    at com.taobao.arthas.core.server.ArthasBootstrap.<init>(ArthasBootstrap.java:114)
    at com.taobao.arthas.core.server.ArthasBootstrap.getInstance(ArthasBootstrap.java:449)
    at com.taobao.arthas.core.server.ArthasBootstrap.getInstance(ArthasBootstrap.java:437)
    ... 7 more

回答

7

这个 ShutdownHookAssembler有点奇怪。

为什么不直接用

Runtime.getRuntime().addShutdownHook(new Thread());

另外,测试arthas 3.5.0没有问题。

7

我们有一个平滑下线功能,需要先执行这个下线操作,切断外部的请求,然后再执行其他关机钩子,所以放在了0个slot,我试下3.5.0的版本看下,

2

我们有一个平滑下线功能,需要先执行这个下线操作,切断外部的请求,然后再执行其他关机钩子,所以放在了0个slot,我试下3.5.0的版本看下,

8

另外,本质上这个和arthas没关系。 arthas只是代码里调用了 if (System.console() != null) { ,然后 jvm的代码自己注册的 slot 0的 shutdown hook。这个应该是 jvm自己留的。其它的 shutdown hook 都是从slot 1开始的,jvm自己的代码就有体现。

5

好的 ,灰常感谢