GraalVM可以对Java应用进行二进制编译,从而生成可执行文件。按照GraalVM官方说法和一般的认知,编译后的可执行文件应该比虚拟机解释执行的Java运行更快。但是似乎情况也不都是如此,请看以下测试结果。

测试环境

测试环境如下,我使用以下4种Java虚拟机作为性能对比,同时还使用GravvlVM编译Java应用,对比可执行应用性能。

  • RHEL 7.6 on BareMetal
  • GravvlVM 19.2.1(基于OpenJDK 1.8.0_232 64bit)
  • GravvlVM 19.3.0(基于OpenJDK 11.0.5 64bit)
  • JDK Hotsport 1.8.0_232 64bit
  • JDK Hotsport 13.0.1 64bit
  • OpenJDK 13.0.1 64bit

测试1

测试代码

测试程序很简单,就是下面这段GraalVM官方提供的基于Java的测试代码。为了减少偶然性因素,我下面的测试代码将源代码的循环次数扩大了10倍。

public class CountUppercase {
    static final int ITERATIONS = Math.max(Integer.getInteger("iterations", 1), 1);
    public static void main(String[] args) {
        String sentence = String.join(" ", args);
        for (int iter = 0; iter < ITERATIONS; iter++) {
            if (ITERATIONS != 1) System.out.println("-- iteration " + (iter + 1) + " --");
            long total = 0, start = System.currentTimeMillis(), last = start;

            for (int i = 1; i < 100_000_000; i++) {
                total += sentence.chars().filter(Character::isUpperCase).count();
                if (i % 10_000_000 == 0) {
                    long now = System.currentTimeMillis();
                    System.out.printf("%d (%d ms)%n", i / 10_000_000, now - last);
                    last = now;
                }
            }
            System.out.printf("total: %d (%d ms)%n", total, System.currentTimeMillis() - start);
        }
    }
}

测试结果

  1. 采用GravvlVM 19.2.1编译Java Class,然后采用GravvlVM 19.2.1、GravvlVM 19.3.0、JDK Hotspot 1.8.0_232、JDK Hotspot 13.0.1和OpenJDK13.0.1运行应用。执行参数为“In 2019 I would like to run ALL languages in one VM.”。每种JavaVM都记录5次测试结果(JDK Hotspot 1.8、JDK Hotspot 13和OpenJDK 13的测试结果有较大抖动,所以多测试了3组)。
    在这里插入图片描述
  2. 采用GravvlVM 19.3.0编译Java Class,然后采用GravvlVM 19.3.0、JDK Hotspot 13.0.1和OpenJDK13.0.1运行应用。执行参数为“In 2019 I would like to run ALL languages in one VM.”。每种JavaVM都记录5次测试结果。
    在这里插入图片描述
  3. 采用JDK Hotspot 1.8.0_232编译Java Class,然后采用GravvlVM 19.2.1、GravvlVM 19.3.0、JDK Hotspot 1.8.0_232、JDK Hotspot 13.0.1和OpenJDK13.0.1运行应用。执行参数为“In 2019 I would like to run ALL languages in one VM.”。每种JavaVM都记录5次测试结果(JDK Hotspot 1.8、JDK Hotspot 13和OpenJDK 13的测试结果有较大抖动,所以多测试了3组)。
    在这里插入图片描述
  4. 采用JDK Hotspot 13.0.1编译Java Class,然后采用JDK Hotspot 13.0.1和OpenJDK13.0.1运行应用。执行参数为“In 2019 I would like to run ALL languages in one VM.”。每种JavaVM都记录8次测试结果。
    在这里插入图片描述
  5. 采用GravvlVM 19.2.1编译Java Class,然后采用GravvlVM 19.2.1的nateve-image编译Java Class至可执行应用。然后测试可执行应用性能,执行参数为“In 2019 I would like to run ALL languages in one VM.”。
    在这里插入图片描述
  6. 采用GravvlVM 19.3.0编译Java Class,然后采用GravvlVM 19.3.0的nateve-image编译Java Class至可执行应用。然后测试可执行应用性能,执行参数为“In 2019 I would like to run ALL languages in one VM.”。
    在这里插入图片描述

测试2

测试代码

将代码中的循环减少到100次。然后在进行测试,本测试只GraalVM 19.3.0的native-image和JDK Hotspot 13.0.1的执行的执行时间。

public class CountUppercase {
    static final int ITERATIONS = Math.max(Integer.getInteger("iterations", 1), 1);
    public static void main(String[] args) {
        String sentence = String.join(" ", args);
        for (int iter = 0; iter < ITERATIONS; iter++) {
            if (ITERATIONS != 1) System.out.println("-- iteration " + (iter + 1) + " --");
            long total = 0, start = System.currentTimeMillis(), last = start;
            for (int i = 1; i < 100; i++) {
                total += sentence.chars().filter(Character::isUpperCase).count();
                if (i % 10 == 0) {
                    long now = System.currentTimeMillis();
                    System.out.printf("%d (%d ms)%n", i / 10, now - last);
                    last = now;
                }
            }
            System.out.printf("total: %d (%d ms)%n", total, System.currentTimeMillis() - start);
        }
    }
}

测试结果

可以看到可执行文件的执行速度要比Java Class执行的要快很多。

$ time countuppercase In 2019 I would like to run ALL languages in one VM.
1 (0 ms)
2 (0 ms)
3 (0 ms)
4 (0 ms)
5 (0 ms)
6 (0 ms)
7 (1 ms)
8 (0 ms)
9 (0 ms)
total: 693 (1 ms)

real	0m0.003s
user	0m0.000s
sys		0m0.003s

$ time ~/graalvm/bin/java CountUppercase In 2019 I would like to run ALL languages in one VM.
1 (4 ms)
2 (23 ms)
3 (0 ms)
4 (1 ms)
5 (0 ms)
6 (1 ms)
7 (0 ms)
8 (1 ms)
9 (0 ms)
total: 693 (31 ms)

real	0m0.143s
user	0m0.182s
sys		0m0.053s

从测试结果分析JVM和native-image使用策率

  1. 不管是否是通过GraalVM编译的Java Class,GraalVM运行这些Java应用都要比Oracle的Hotspot以及开原的OpenJDK快,而且优势较为明显。
  2. 不清楚为什么非GraalVM的运行结果都有明显抖动,用时差能有50%甚至还多,抖动比较明显。用同版本JDK编译后再运行,测试抖动情况少了一些,但是没有彻底根除。我已经排除了JVM垃圾回收的因素,但还找不到原因。至少经验是如果是Hotspot或OpenJDK,编译用是什么版本,运行就用什么版本。
  3. 虽然GraalVM19.3.0比GraalVM19.2.1的native-image的编译和执行速度快些。
  4. 经GraalVM的native-image编译后可执行文件并非在任何场景都比Java Class快。前者的主要优势在于超快的启动速度(这一优势尤其适合结合Container实现基于事件的响应处理、Serverless架构等,从而有很好的响应速度和弹性扩展速度),而大量的循环处理反而倒是后者快(还没想明白为什么可执行应用的循环会慢)。至于到底该不该用native-image将Java Class编译成二进制可执行应用?最好还是测试一下!!!
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐