强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第5章:GraalVM

第5章:GraalVM

“GraalVM 代表了 JIT 编译的未来方向——一个运行时,多种语言,共享优化基础设施。”

5.1 GraalVM 概述

GraalVM 是 Oracle 开发的高性能多语言虚拟机,它基于 JVM 生态,但远超传统 JVM。GraalVM 的核心创新在于:通过统一的编译器基础设施,为多种编程语言提供高性能 JIT 编译。

5.1.1 GraalVM 的版本和分发

版本说明授权
Community Edition (CE)开源版本GPL v2
Enterprise Edition (EE)商业版本商业许可
Standard Edition标准 JDK 兼容BCL
# 安装 GraalVM (以 SDKMAN 为例)
sdk install java 21.0.2-graal

# 或使用 GraalVM Updater
gu install native-image
gu install python
gu install ruby
gu install nodejs

5.1.2 GraalVM 架构

┌─────────────────────────────────────────────────────────────────┐
│                       GraalVM 架构                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Java    JavaScript  Python   Ruby   R   LLVM Languages        │
│    │         │         │       │     │       │                  │
│    └─────────┴─────────┴───────┴─────┴───────┘                  │
│                         │                                        │
│                  ┌──────┴──────┐                                 │
│                  │   Truffle   │  ← 语言实现框架                 │
│                  │  Framework  │                                 │
│                  └──────┬──────┘                                 │
│                         │                                        │
│                  ┌──────┴──────┐                                 │
│                  │   Graal     │  ← 高性能 JIT 编译器           │
│                  │  Compiler   │     (通用优化基础设施)           │
│                  └──────┬──────┘                                 │
│                         │                                        │
│           ┌─────────────┼─────────────┐                          │
│           │             │             │                          │
│           ▼             ▼             ▼                          │
│     ┌──────────┐  ┌──────────┐  ┌──────────┐                   │
│     │   JVM    │  │  Native  │  │  LLVM    │                   │
│     │   模式   │  │  Image   │  │  模式    │                   │
│     └──────────┘  └──────────┘  └──────────┘                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.2 Truffle 框架

5.2.1 什么是 Truffle

Truffle 是一个用 Java 编写的语言实现框架。它让语言开发者能够用相对简单的解释器实现获得自动的 JIT 编译优化。

传统方式:
  语言 → 自定义解释器 → 自己实现 JIT (大量工作)

Truffle 方式:
  语言 → Truffle AST 解释器 → 自动被 Graal 编译为机器码

5.2.2 Truffle 的核心思想

┌─────────────────────────────────────────────────────────────────┐
│                    Truffle 部分求值 (PE)                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 语言开发者编写 AST 解释器(普通 Java 代码)                  │
│                                                                 │
│  2. 当代码变热时,Graal 对解释器进行"部分求值"                   │
│     └─ 将"解释的程序"作为常量折叠进解释器代码                    │
│                                                                 │
│  3. 结果:解释器循环被消除,得到直接操作数据的机器码              │
│     └─ 等效于手写的专门编译器                                    │
│                                                                 │
│  示例:                                                          │
│  解释器: execute(node) → { switch(node.type) { ... } }          │
│  对特定程序 PE 后: 直接内联具体节点的操作                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.2.3 使用 Truffle 实现简单语言

// SimpleLanguage 节点定义示例
@NodeInfo(shortName = "add")
public class AddNode extends ExpressionNode {

    @Child private ExpressionNode leftNode;
    @Child private ExpressionNode rightNode;

    public AddNode(ExpressionNode left, ExpressionNode right) {
        this.leftNode = left;
        this.rightNode = right;
    }

    @Specialization
    protected long add(long left, long right) {
        return left + right;
    }

    @Specialization
    protected double add(double left, double right) {
        return left + right;
    }

    @Specialization
    protected String add(String left, String right) {
        return left + right;
    }
}

// 整数节点
@NodeInfo(shortName = "integer")
public class LongLiteralNode extends ExpressionNode {

    private final long value;

    public LongLiteralNode(long value) {
        this.value = value;
    }

    @Override
    public long executeLong(VirtualFrame frame) {
        return value;
    }
}

5.2.4 Truffle AST 执行模型

// 完整的简单表达式语言

// 表达式节点基类
public abstract class ExpressionNode extends Node {
    public abstract Object executeGeneric(VirtualFrame frame);

    public long executeLong(VirtualFrame frame) {
        return (long) executeGeneric(frame);
    }

    public double executeDouble(VirtualFrame frame) {
        return (double) executeGeneric(frame);
    }
}

// 变量读取
public class ReadLocalVarNode extends ExpressionNode {
    private final FrameSlot slot;

    public ReadLocalVarNode(FrameSlot slot) {
        this.slot = slot;
    }

    @Override
    public Object executeGeneric(VirtualFrame frame) {
        return frame.getValue(slot);
    }
}

// 函数调用
public class InvokeNode extends ExpressionNode {
    @Child private ExpressionNode functionNode;
    @Children private ExpressionNode[] argumentNodes;

    @Override
    public Object executeGeneric(VirtualFrame frame) {
        // 获取函数
        SLFunction function = (SLFunction) functionNode.executeGeneric(frame);

        // 求值参数
        Object[] arguments = new Object[argumentNodes.length];
        for (int i = 0; i < argumentNodes.length; i++) {
            arguments[i] = argumentNodes[i].executeGeneric(frame);
        }

        // 调用函数
        return function.call(arguments);
    }
}

5.2.5 Truffle 的自动优化

┌─────────────────────────────────────────────────────────────────┐
│                Truffle 自动获得的优化                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ✓ 部分求值 (Partial Evaluation)                                │
│    └─ 解释器循环消除,AST 被内联                                 │
│                                                                 │
│  ✓ 类型特化 (Type Specialization)                               │
│    └─ 基于 @Specialization 注解自动生成类型特定代码             │
│                                                                 │
│  ✓ 内联缓存 (Inline Cache)                                      │
│    └─ 自动在类型检查处插入缓存                                   │
│                                                                 │
│  ✓ 逃逸分析 (Escape Analysis)                                   │
│    └─ Graal 编译器级别的逃逸分析                                 │
│                                                                 │
│  ✓ SIMD 向量化 (Auto-vectorization)                             │
│    └─ 自动将循环转换为 SIMD 指令                                 │
│                                                                 │
│  ✓ 栈上替换 (On-Stack Replacement)                              │
│    └─ 运行中代码的无缝切换                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.3 Graal 编译器

5.3.1 Graal vs C2

特性GraalC2 (HotSpot)
IR 类型Sea of NodesSea of Nodes
实现语言JavaC++
内存安全
逃逸分析部分逃逸分析全局逃逸分析
语言支持多语言Java 为主
可扩展性
编译速度稍慢稍快
优化效果优秀良好

5.3.2 Graal 编译管线

// 使用 Graal 编译 Java 代码
public class GraalOptimizationDemo {

    // Graal 擅长的优化:部分逃逸分析
    public int partialEscapeExample(boolean condition) {
        // 这个对象在 condition=false 时不会被分配
        Point p = new Point(1, 2);
        if (condition) {
            return p.x + p.y;  // 对象被标量替换
        }
        return 0;  // 对象完全消除
    }

    // 循环优化
    public long loopExample(int[] arr) {
        long sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
        // Graal 会进行:
        // 1. 边界检查消除
        // 2. 循环展开
        // 3. SIMD 向量化
    }

    // 内联示例
    public int inlineExample(int x) {
        return add(multiply(x, 2), 1);
    }

    private int multiply(int a, int b) { return a * b; }
    private int add(int a, int b) { return a + b; }
    // Graal 会将 multiply 和 add 内联
    // 最终代码等价于: return x * 2 + 1;
}

5.3.3 使用 Graal 编译器

# 使用 Graal 编译器替代 C2
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseJVMCICompiler \
     -XX:+EnableJVMCI \
     MyApp

# 查看 Graal 编译日志
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseJVMCICompiler \
     -XX:+TraceCompilation \
     -XX:+PrintCompilation \
     MyApp

# 使用 Ideal Graph Visualizer
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseJVMCICompiler \
     -Dgraal.Dump=:1 \
     MyApp

5.4 多语言支持

5.4.1 GraalVM 支持的语言

┌─────────────────────────────────────────────────────────────────┐
│                  GraalVM 支持的语言                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  JVM 语言                                                       │
│  ├─ Java                                                        │
│  ├─ Kotlin                                                      │
│  ├─ Scala                                                       │
│  └─ Clojure                                                     │
│                                                                 │
│  Truffle 语言                                                   │
│  ├─ JavaScript (GraalJS)                                        │
│  ├─ Python (GraalPython)                                        │
│  ├─ Ruby (TruffleRuby)                                          │
│  ├─ R (FastR)                                                   │
│  └─ LLVM Languages (Sulong)                                     │
│     ├─ C                                                        │
│     ├─ C++                                                      │
│     └─ Rust                                                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.4.2 多语言互操作(Polyglot API)

// Java 中调用 JavaScript
import org.graalvm.polyglot.*;

public class PolyglotDemo {
    public static void main(String[] args) {
        // 创建 JavaScript 上下文
        try (Context context = Context.create("js")) {
            // 执行 JavaScript 代码
            Value result = context.eval("js",
                "function add(a, b) { return a + b; }" +
                "add(3, 4);"
            );
            System.out.println("结果: " + result.asInt());  // 7

            // 获取 JavaScript 函数
            Value add = context.getBindings("js").getMember("add");
            int sum = add.execute(10, 20).asInt();
            System.out.println("10 + 20 = " + sum);  // 30
        }
    }
}
// JavaScript 中调用 Java
const ArrayList = Java.type('java.util.ArrayList');
const list = new ArrayList();
list.add('hello');
list.add('world');

// 调用 Java 方法
const Stream = Java.type('java.util.stream.Stream');
const result = Stream.of(1, 2, 3, 4, 5)
    .map(x => x * x)
    .reduce(0, (a, b) => a + b);
print(result);  // 55
# Python 中调用 Java
import java

ArrayList = java.type('java.util.ArrayList')
list = ArrayList()
list.add('hello')
list.add('world')

# 调用 Python 函数从 Java
HashMap = java.type('java.util.HashMap')
m = HashMap()
m.put('key', 'value')
print(m.get('key'))

5.4.3 共享数据结构

// 跨语言共享数据
public class SharedDataDemo {
    public static void main(String[] args) {
        try (Context context = Context.newBuilder()
                .allowAllAccess(true)
                .build()) {

            // 创建共享对象
            context.getBindings("js").putMember("sharedData",
                new SharedObject());

            // 在 JavaScript 中使用
            context.eval("js",
                "sharedData.increment();" +
                "sharedData.increment();" +
                "print(sharedData.getCount());"  // 2
            );

            // 在 Python 中继续使用
            context.eval("python",
                "sharedData.increment()" +
                "print(sharedData.get_count())"  // 3
            );
        }
    }
}

public class SharedObject {
    private int count = 0;

    public void increment() { count++; }
    public int getCount() { return count; }
}

5.5 AOT 编译与原生镜像

5.5.1 Native Image 概述

Native Image 是 GraalVM 的 AOT 编译工具,可以将 Java 应用编译为独立的原生可执行文件。

┌─────────────────────────────────────────────────────────────────┐
│                    Native Image 编译流程                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Java 应用 (JAR)                                                │
│       │                                                         │
│       ▼                                                         │
│  ┌─────────────┐                                                │
│  │ 静态分析    │  ← 分析可达代码                                │
│  └──────┬──────┘                                                │
│         ▼                                                       │
│  ┌─────────────┐                                                │
│  │ 初始化      │  ← 在编译时执行 <clinit>                       │
│  └──────┬──────┘                                                │
│         ▼                                                       │
│  ┌─────────────┐                                                │
│  │ 优化编译    │  ← Graal 编译器优化                            │
│  └──────┬──────┘                                                │
│         ▼                                                       │
│  ┌─────────────┐                                                │
│  │ 代码生成    │  ← 生成机器码                                  │
│  └──────┬──────┘                                                │
│         ▼                                                       │
│  原生可执行文件                                                  │
│  (包含应用 + Substrate VM + GC)                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.5.2 构建原生镜像

// 示例 Spring Boot 应用
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, Native!";
    }
}

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
<!-- pom.xml 配置 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <version>0.10.1</version>
            <configuration>
                <mainClass>com.example.Application</mainClass>
            </configuration>
        </plugin>
    </plugins>
</build>
# 使用 Maven 构建原生镜像
mvn -Pnative native:compile

# 或使用 GraalVM 工具
native-image \
    -jar target/app.jar \
    -o app \
    --no-fallback \
    -H:ReflectionConfigurationFiles=reflect-config.json

# 运行原生应用
./app

5.5.5 Native Image 的限制

限制说明解决方案
反射编译时需声明反射配置文件
动态代理编译时需声明代理配置
类路径加载不支持动态加载预先注册资源
JNI需要配置JNI 配置
序列化受限支持序列化配置
Unsafe受限替换为安全 API
// reflect-config.json
[
    {
        "name": "com.example.MyClass",
        "allDeclaredMethods": true,
        "allDeclaredConstructors": true
    },
    {
        "name": "com.example.AnotherClass",
        "methods": [
            { "name": "doSomething", "parameterTypes": [] }
        ]
    }
]

5.5.4 性能对比

┌─────────────────────────────────────────────────────────────────┐
│              JVM vs Native Image 性能对比                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  指标              JVM 模式         Native Image                 │
│  ─────────────────────────────────────────────────              │
│  启动时间          1-5 秒          10-50 毫秒                   │
│  内存占用          100-300 MB      20-50 MB                     │
│  二进制大小        N/A (JAR)       50-100 MB                    │
│  峰值吞吐量        高              中等                         │
│  预热时间          需要             无需                         │
│                                                                 │
│  适用场景:                                                      │
│  JVM: 长期运行的服务、计算密集型应用                             │
│  Native: CLI 工具、Serverless、微服务                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.6 GraalVM 适用场景

5.6.1 最佳场景

┌─────────────────────────────────────────────────────────────────┐
│                   GraalVM 最佳应用场景                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 多语言应用                                                  │
│     └─ 需要混合使用 Java + JS + Python 的场景                   │
│                                                                 │
│  2. 高性能微服务                                                │
│     └─ Spring Boot + Native Image                              │
│                                                                 │
│  3. Serverless 函数                                             │
│     └─ 冷启动敏感,需要快速启动                                  │
│                                                                 │
│  4. CLI 工具                                                    │
│     └─ 需要独立可执行文件,无 JVM 依赖                          │
│                                                                 │
│  5. 替代 C2 编译器                                              │
│     └─ 需要更好的优化效果                                       │
│                                                                 │
│  6. 语言实现                                                    │
│     └─ 快速获得 JIT 编译能力                                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.6.2 Serverless 示例

// AWS Lambda with GraalVM Native Image
public class LambdaHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
        String name = input.getQueryStringParameters().getOrDefault("name", "World");

        return new APIGatewayProxyResponseEvent()
            .withStatusCode(200)
            .withBody("{\"message\": \"Hello, " + name + "!\"}")
            .withHeaders(Map.of("Content-Type", "application/json"));
    }
}
# 构建 Lambda Native Image
native-image \
    --initialize-at-build-time \
    -H:+ReportExceptionStackTraces \
    -jar lambda.jar \
    bootstrap

# 打包部署
zip function.zip bootstrap
aws lambda create-function \
    --function-name my-function \
    --runtime provided.al2023 \
    --handler bootstrap \
    --zip-file fileb://function.zip

5.7 与其他 JIT 对比

特性GraalVMHotSpot C2V8
语言支持多语言JavaJavaScript
AOT 支持Native Image有限
编译器语言JavaC++C++
部分逃逸分析
可嵌入性中等
社区生态成长中成熟成熟

5.8 本章小结

关键要点

  1. Truffle 框架:让语言实现者用解释器代码获得 JIT 性能
  2. Graal 编译器:先进的优化技术,包括部分逃逸分析
  3. 多语言互操作:Polyglot API 实现无缝跨语言调用
  4. Native Image:AOT 编译,快速启动,低内存
  5. 适用场景:多语言应用、微服务、Serverless、CLI

选择建议

  • 需要多语言互操作 → GraalVM Polyglot
  • 需要快速启动 → Native Image
  • 需要极致性能 → Graal JIT (JVM 模式)
  • 实现新语言 → Truffle 框架

5.9 扩展阅读


上一章: 第4章 - V8 引擎 下一章: 第6章 - LLVM JIT