【问题标题】:Java agent packaged as fatjar runs slowly with JDK8打包为 fatjar 的 Java 代理使用 JDK8 运行缓慢
【发布时间】:2021-12-01 11:11:34
【问题描述】:

我们正在使用 Java 代理来增强应用程序中的一些方法。代理依赖于其他jar,为了方便,我们将代理和依赖打包成一个fatjar,然后通过自定义的类加载器加载依赖的jar。程序可以正常启动,但是很慢,完全启动需要几分钟。 (我统计了每个类的加载时间(在 ClassLoader 中的 loadclass()),发现有些类的加载需要几百毫秒)。同样,当我使用spring boot(也是具有相同依赖项的fatjar)时,它会快得多。

JDK的版本是1.8.0_261。

premain方法如下:

public static void premain(String args, Instrumentation instrumentation) throws Exception {
    final AgentLanucher agentLauncher = new AgentLanucher();   //----1
    agentLauncher.launch();                                    //----2

    // add transformer
    instrumentation.addTransformer(new CustomAgentTransformer);
}

而AgentLauncher的代码如下:

import org.springframework.boot.loader.JarLauncher;
public class AgentLanucher extends JarLauncher {
    @Override
    protected void launch(String[] args) throws Exception{
        super.launch(args);
    }
}

在执行标有 (1,2) 的代码时,加载一个类需要很长时间(几十或几百毫秒)。计算加载一个类的时间代码如下:

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
    final long start = System.currentTimeMillis();
    final Class<?> clazz = super.loadClass(name, resolve);
    final long end = System.currentTimeMillis();
    System.out.println("Load [" + name + "] : " + (end -start));
    return clazz;
}

而且我发现当程序用JDK11运行时,它运行得很快。然后我将版本改回JDK8,但将代码从premain移到了一个单独的线程,它也运行得很快。代码如下:

public static void premain(String args, Instrumentation instrumentation) {
    CompletableFuture.runAsync(()->{
        final AgentLauncher launcher = new AgentLauncher();
        launcher.launch();
        instrumentation.addTransformer(new CustomAgentTransformer());
    });
}

还有一次尝试,我阻塞了主线程,直到子线程完成。它运行缓慢。代码如下:

public static void premain(String args, Instrumentation instrumentation) {
    final CompletableFuture<Void> initTask = CompletableFuture.runAsync(() -> {
        final AgentLauncher launcher = new AgentLauncher();
        launcher.launch();
        instrumentation.addTransformer(new CustomAgentTransformer());
    });
    System.out.println("Waiting.....");
    initTask.join();
}

我对这个问题感到困惑,感谢您的帮助。

【问题讨论】:

  • 什么样的代理?没有代理快吗?什么是代理检测以及它是如何检测的?您是否已经使用分析器来找出时间花费在哪里?
  • 感谢您的提醒。我修改了问题。应用程序快速启动,无需代理。但让我疑惑的是,在instrumentation之前执行代码时,类会需要很长时间才能加载。
  • 使用模块化 java 可以部署一个没有 fat jar 的应用程序(也许还有你的自定义类加载器),如果没有人想出更好的理由来提高你的性能,我会尝试。
  • 包含版本信息总是有帮助的......
  • 感谢您的建议。我已经添加了 JDK 的版本信息以及有关该问题的更多描述。并且由于某种原因,我必须使用 jdk8 运行代码,并且无法使用 jdk 中的模块化。谢谢。

标签: java jvm classloader javaagents fatjar


【解决方案1】:

在 OpenJDK 8 中,JIT 编译器在 JVM TI 代理之后初始化。这意味着,premain 方法始终以解释(慢)模式运行。

这是JDK-7018422 的错误。

模块系统的初始化顺序已更改,因此premain 可以在现代 JDK 中进行 JIT 编译。

【讨论】:

    猜你喜欢
    • 2019-06-24
    • 1970-01-01
    • 1970-01-01
    • 2017-11-19
    • 1970-01-01
    • 2014-09-29
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多