动态字节码支持
我们知道aot是可以支持lambda的。而且从aot的日志上我们可以看出他把我们写的代码已经做了加载。lambda也是动态生成字节码的典型了,是不是动态生成的,aot也会全量支持呢。我们下面来验证这个问题。
lambda是核心类库,所以这次我们把核心类库也编译了。以防误差。
jaotc --output libjava.base.so --module java.base
编写一个lambda的简单demo。
public class AotTest {
public static void main(String[] args) throws Exception {
MathOperation addition = (int a, int b) -> a + b;
addition.operation(1, 2);
}
}
interface MathOperation {
int operation(int a, int b);
}
我们可以通过增加参数,把动态生成的字节码打印成文件。利用-Djdk.internal.lambda.dumpProxyClasses。
利用类加载来打印类加载信息。-verbose:class。
打印aot的信息。-XX:+PrintAOT
把程序输出重定向到一个文件里去。
java -verbose:class -XX:+PrintAOT -Djdk.internal.lambda.dumpProxyClasses=. -XX:AOTLibrary=./libtest.so,./libjava.base.so AotTest > 1.txt
我把动态生成的字节码就放在当前路径了,我们可以看到文件夹下多了一个AotTest$$Lambda$1.class。
我们在日志中可以搜到类加载信息。
[0.339s][info][class,load] AotTest$$Lambda$1/0x0000000800067840 source: AotTest
但是搜不到aot的信息。
说明aot本身对于动态生成的字节码无法预先处理,哪怕他是jdk的核心类库。
动态注入
如果使用了-javaagent加入的监控修改了字节码会是什么表现呢?
我们使用了字节码注入的agent demo。下面是个开源版本。
注入agent
这里一定要注意一个点,这个工具是通过asm做的,他可以打印方法的运行时间。**使用时要把asm的jar包换成一个对应jdk的版本,目前项目用的6,java11得升级。**否则你会发现神奇的错误,那个错误妙不可言。
java -Xbootclasspath/a:asm-8.0.1.jar:asm-analysis-8.0.1.jar:asm-commons-8.0.1.jar:asm-tree-8.0.1.jar -javaagent:trace-0.0.1-SNAPSHOT-agent.jar=Test -XX:AOTLibrary=./libtest.so -XX:+PrintAOT AotTest
通过这个启动参数。我这里只注入我的一个测试类。看看他的方法打印的结果,以及aot的表现。
加agent日志
179 1 aot[ 1] AotTest.lambda$main$0(II)I
179 2 aot[ 1] AotTest.<init>()V
this is TestB
[Ljava.lang.String; main cost 0(这里是agent输出,单位是毫秒)
不加agent
11 1 loaded ./libtest.so aot library
107 1 aot[ 1] AotTest.lambda$main$0(II)I
107 2 aot[ 1] AotTest.<init>()V
107 3 aot[ 1] AotTest.main([Ljava/lang/String;)V
108 4 aot[ 1] TestB.<init>()V
108 5 aot[ 1] TestB.main([Ljava/lang/String;)V
this is TestB
结果发现testB不见了,也就是说agent注入改造后的类是无法使用aot的效果的。
实战编译math3
我们下面编译math3来展示常用的方式。
我们先尝试编译
jaotc --output libmath.so --jar commons-math3-3.6.1.jar
你会发现这个会报错。
Error: Failed compilation: org.apache.commons.math3.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer.bobyqb([D[D)D: org.graalvm.compiler.core.common.PermanentBailoutException: Too many loops in method
Error: Failed compilation: org.apache.commons.math3.optimization.direct.BOBYQAOptimizer.bobyqb([D[D)D: org.graalvm.compiler.core.common.PermanentBailoutException: Too many loops in method
这个情况是我们比较常见的,aot现在还不能匹配所有的场景。我们最简单的就是去掉这个,保证我们编译成功。我们可以使用exclude来去掉不可编译选项。
编写一个文件命名随意,我这里用 exclude.txt,把不能编译的方法都做一下去除。
exclude org.apache.commons.math3.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer.bobyqb([D[D)D
exclude org.apache.commons.math3.optimization.direct.BOBYQAOptimizer.bobyqb([D[D)D
这次尝试编译的话,我们使用–compile-commands把我们指定的规则加上去。
jaotc --output libmath.so --compile-commands exclude.txt --jar commons-math3-3.6.1.jar
再次执行,可以生成对应的libmath.so。
exclude掉一些方法,只要那个不是什么热点方法,其实影响不会太大。我们只要保证大部分的,热点的代码,可以利用aot加速,程序的启动速度就会得到很大的提升。所以这里并不用太担心影响。
有exclude,所以同时也有compileOnly。目的就是比较好的选择出可以编译的选项。
常用参数
--compile-for-tiered Generate profiling code for tiered compilation
在上面介绍的一些参数外,一般编译时还加上面这个参数。这个参数上标注了以后可能会去除,说明那时候aot有更好的解决方案了。在没有对应的解决方案出来时,先都加着吧。