1

我们正在使用 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();
}

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

4

1 回答 1

3

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

这是错误JDK-7018422

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

于 2021-12-05T23:02:17.277 回答