You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何简单小型Java代码在Graal JVM上比Oracle JVM快30倍?

常规JVM与GraalVM执行性能差异问题解析

问题背景

未使用GraalVM的native-image工具编译代码,仅通过GraalVM和常规Oracle JVM运行同一个Java类(字节码完全一致)。无论Java版本或平台(已测试Linux、Mac),GraalVM的运行速度始终比常规JVM快30倍,推测常规JVM的JIT编译器未正确优化这个简单的小型方法,希望明确原因并找到常规JVM下的解决方案(目前临时方案是迁移到GraalVM)。

复现代码

public class OracleJvm23MathBug {

    // 仅包含少量简单数学运算
    // =====> 理应被优化/编译/内联!
    private static final long doSomething(int load, int i) {
        long x = 0;
        for (int j = 0; j < load; j++) {
            long pow = (i % 8) * (i % 16);
            if (i % 2 == 0) {
                x += pow;
            } else {
                x -= pow;
            }
        }
        return x;
    }

    /*
     * 使用OpenJDK/Zulu/Oracle JVM 23执行 => 平均215纳秒
     * 使用Graal23 JVM 23执行 => 平均7纳秒
     * 
     * 该问题在所有平台均可复现(已测试Linux和Mac)
     * 
     * $ java -version 
     * java version "23.0.1" 2024-10-15 
     * Java(TM) SE Runtime Environment (build 23.0.1+11-39)
     * Java HotSpot(TM) 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing)
     * 
     * $ java -cp . OracleJvm23MathBug
     * 计算结果: -550000000000
     * 测量数据: 10000000| 平均时间: 215纳秒 | 最短时间: 83纳秒 | 最长时间: 199750纳秒
     * 
     * $ java -version
     * java version "23.0.1" 2024-10-15
     * Java(TM) SE Runtime Environment Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01)
     * Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01, mixed mode, sharing)
     * 
     * $ java -cp . OracleJvm23MathBug
     * 计算结果: -550000000000
     * 测量数据: 10000000| 平均时间:7纳秒 | 最短时间:0纳秒 | 最长时间:178625纳秒
     */
    public static final void main(String[] args) {
        final int iterations = 10_000_000;
        final int load = 10_000;
        NanoBench bench = new NanoBench();
        long computed = 0;
        for (int i = 0; i < iterations; i++) {
            bench.mark();
            computed += doSomething(load, i);
            bench.measure();
        }
        System.out.println("计算结果: " + computed);
        bench.printResults();
    }

    private static class NanoBench {

        private int measurements;
        private long totalTime, minTime, maxTime, time;
        private final StringBuilder sb = new StringBuilder(128);

        NanoBench() {
            reset();
        }

        public final void reset() {
            totalTime = time = measurements = 0;
            maxTime = Long.MIN_VALUE;
            minTime = Long.MAX_VALUE;
        }

        public final void mark() {
            time = System.nanoTime();
        }

        public final void measure() {
            long lastNanoTime = System.nanoTime() - time;
            totalTime += lastNanoTime;
            minTime = lastNanoTime < minTime ? lastNanoTime : minTime;
            maxTime = lastNanoTime > maxTime ? lastNanoTime : maxTime;
            measurements++;
        }

        public final void printResults() {
            sb.setLength(0);
            sb.append("测量数据: ").append(measurements);
            sb.append("| 平均时间: ").append((long) (totalTime / (double) measurements)).append("纳秒");
            sb.append(" | 最短时间: ").append(minTime).append("纳秒");
            sb.append(" | 最长时间: ").append(maxTime).append("纳秒\n\n");
            for (int i = 0; i < sb.length(); i++) System.out.print(sb.charAt(i));
        }
    }
}

原因分析

1. 循环不变量优化能力差异

doSomething方法的内层循环中,(i % 8) * (i % 16)和i % 2 == 0的结果在j的整个迭代周期内完全固定(因为i是外层循环变量,内层循环不会修改i)。GraalVM的JIT编译器能彻底识别这些循环不变量,将其提取到循环外,并进一步把整个内层循环简化为单次算术运算(即x = pow * load或x = -pow * load),相当于完全消除了内层循环。而常规HotSpot JVM的C2编译器可能因启发式规则限制,未完成这一极致优化,导致每次循环迭代都重复计算不变量。

2. 优化策略激进程度不同

GraalVM默认的优化策略比HotSpot C2更激进,对内层小方法的内联、常量传播等优化步骤的触发条件更宽松。HotSpot可能因方法调用次数阈值、循环复杂度判断等因素,未触发最高级别的优化流程。

常规JVM下的解决方案

1. 手动提取循环不变量(最可靠)

直接修改代码,手动将循环内的不变量提前计算,彻底消除内层循环:

private static final long doSomething(int load, int i) {
    long pow = (i % 8) * (i % 16);
    long multiplier = (i % 2 == 0) ? 1 : -1;
    return pow * multiplier * load;
}

修改后,无论哪种JVM都能直接计算结果,性能可与GraalVM原表现持平。

2. 调整HotSpot JVM优化参数

通过JVM参数强制触发更激进的优化:

  • -XX:+AggressiveOpts:启用HotSpot的激进优化集合
  • -XX:CompileThreshold=1000:降低JIT编译触发的方法调用次数阈值,让doSomething更快进入编译优化流程
  • -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation:打印编译日志,确认doSomething是否被正确编译和内联

内容的提问来源于stack exchange,提问作者user19401035

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:05:03