V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gengzi
V2EX  ›  Java

Java Asm 修改 jdk 源码中的 ThreadPoolExecutor execute 不能引入 不是 jdk 的包下的类吗?

  •  
  •   gengzi · 314 天前 · 1068 次点击
    这是一个创建于 314 天前的主题,其中的信息可能已经有所发展或是发生改变。

    提示 NoClassDefFoundError 。 还是说需要其他方式

    第 1 条附言  ·  313 天前
       public void execute(Runnable var1) {
            // 编译增加的代码
            MDC.setContextMap((Map)MDCInheritableThreadLocal.get());
            // 本身的源码
            if (var1 == null) {
                NullPointerException var10000 = new NullPointerException();
                System.out.println("end");
                throw var10000;
            } else {
                int var2 = this.ctl.get();
                if (workerCountOf(var2) < this.corePoolSize) {
                    if (this.addWorker(var1, true)) {
                        System.out.println("end");
                        return;
                    }
    
                    var2 = this.ctl.get();
                }
         // ...
    }
    

    会提示 NoClassDefFoundError: fun/MDCInheritableThreadLocal

    9 条回复    2021-01-27 10:07:12 +08:00
    wsxyeah
        1
    wsxyeah  
       314 天前 via iPhone
    这些东西是用户机器上的,除非你打包一个 jre 进去
    gengzi
        2
    gengzi  
    OP
       313 天前
    @wsxyeah 我在启用前使用 Agent,修改 jdk ThreadPoolExecutor execute 的方法,引入的 jar 应该会在运行时加载吧。
    ffutop
        3
    ffutop  
       313 天前   ❤️ 1
    ThreadPoolExecutor 是被 Bootstrap ClassLoader 加载的。它的加载路径不包括你自定义的 MDCInheritableThreadLocal 。

    可以用 -Xbootclasspath 指定 Bootstrap 加载特定 Jar 包
    或者在 MANIFEST.MF 用 Boot-Class-Path 声明 Jar 包路径
    kingfalse
        4
    kingfalse  
       313 天前 via Android
    试试 javassist
    fantastM
        5
    fantastM  
       313 天前
    在启动参数里用 -javaagent 的话,确实是如 #3 说的类加载问题。可以用 arthas 的 sc 、classloader 之类的命令来查看类的加载情况
    gengzi
        6
    gengzi  
    OP
       313 天前
    @ffutop @fantastM ok,感谢解答。我去瞅瞅
    gengzi
        7
    gengzi  
    OP
       313 天前
    @ffutop @fantastM 麻烦再咨询下,
    ```
    -javaagent:D:/ideaworkspace/baselog.jar -Xbootclasspath/a:D:/ideaworkspace/baselog.jar
    ```
    当这个键入 idea 中,运行 springboot 工程,提示 NoClassDefFoundError 关于项目中的引入的 jar,那这些 jar 我需要怎么引入。
    fantastM
        8
    fantastM  
       312 天前   ❤️ 1
    简单回答:可以在 agent 中自定义类加载器,避免遵循 Java 类加载器中的双亲委派模型。

    详细回答:
    JVM 加载类是按照双亲委派模型来执行的,每个类都会优先委托给父类加载器来加载,当父类加载器无法加载类的时候再由子类加载器来加载,因此在 JVM 中加载的类会有一种层级关系。

    在你的例子中 `ThreadPoolExecutor` 会由 BootStrap ClassLoader 加载,参数 -javaagent 指定的 baselog.jar 包默认会由 System ClassLoader 加载,所以你一开始描述的问题原因是:`ThreadPoolExecutor` 类在被 BootStrap ClassLoader 加载之后链接的时候,无法找到需要被 System ClassLoader 加载的 baselog.jar 包里的 `MDCInheritableThreadLocal`。(注意这个问题是发生在链接阶段,这也是 JVM 为什么抛了 `NoClassDefFoundError` 而不是 `ClassNotFoundException` 的原因)

    然后你将 baselog.jar 包指定为由 BootStrap ClassLoader 加载,这样的话,`ThreadPoolExecutor` 类在被 BootStrap ClassLoader 加载、链接、初始化的时候,就可以找到同样是被 BootStrap ClassLoader 加载的 `MDCInheritableThreadLocal` 了。

    你在 #7 里说的事情,我理解为是你在 baselog.jar 包里用到了「项目中的引入的 jar 」(可能是 Spring 之类的吧),这部分包不由是 BootStrap ClassLoader 加载的,可能是 System ClassLoader,也可能是 Tomcat 的 Webapp ClassLoader,所以自然也会有 `ClassNotFoundException` 的问题。解决方案的话,我不建议把这类包也指定为由 BootStrap ClassLoader 来加载,更好的解决方案是自定义类加载机制,破坏一下 Java 类加载器中的双亲委派模型。

    具体实现的话,可以借鉴一些分布式追踪 APM 系统,它们在采集应用的数据时候,使用的无侵入式方案也是 javaagent,也会对项目中的一些代码做改造。

    一些可供查阅的资料:
    https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html
    https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
    gengzi
        9
    gengzi  
    OP
       312 天前
    @fantastM 听君一席话胜读十年书。昨天在查阅资料时,也考虑使用自定义的类加载器来尝试,我再去看看这块内容。非常感谢这么详细的解答。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2347 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 15:00 · PVG 23:00 · LAX 07:00 · JFK 10:00
    ♥ Do have faith in what you're doing.