以 Native 的方式运行 Java 程序

43

介绍

Java 由于其机制, 其 Java 程序通常都是由 JVM 来解释运行, 所以运行 Java 程序必须使用 java -jar 或其他命令来运行, 而不能在没有 JVM 的情况下运行

通过 GraalVM 可以使用 native-image 命令将 Java 的 class, module, jar 编译成一个二进制 native 程序, 从而可以不需要 JVM 直接运行

操作方法

通常我们都希望将一个有用的程序进行 native 编译, 所以这里不会讲如何编译 "hello world" 等代码

官方文档: https://www.graalvm.org/latest/guides/

下载 GraalVM

社区版 GraalVM JDK: https://www.oracle.com/java/technologies/downloads/#graalvmjava21

和正常的 JDK 一样的使用方式

使用工具生成配置

由于 Java 的动态代理, 反射是在运行中确定的, native-image 是基于静态分析的, 所以必须提前告知其反射等信息

使用 java -agentlib 来生成配置文件

例如在当前目录下, 生成 demon.jar 的配置文件:

java -agentlib:native-image-agent=config-output-dir=./config -jar demo.jar 

实际上他是把你的程序运行一遍分析

生成后的文件如下:

config
├── jni-config.json
├── predefined-classes-config.json
├── proxy-config.json
├── reflect-config.json
├── resource-config.json
└── serialization-config.json

将这些文件全部复制到项目的 resources/META-INF/org.example/demo 下 (注意换成你自己的包名)

复制后的文件结构如下:

native-demo                                                                     
├── build.gradle.kts
└── src
    ├── main
        ├── java
        │   └── org
        │       └── example
        │           └── demo
        │               ├── Main.java
        └── resources
            └── META-INF
                └── native-image
                    └── org.example
                        └── demo
                            ├── jni-config.json
                            ├── predefined-classes-config.json
                            ├── proxy-config.json
                            ├── reflect-config.json
                            ├── resource-config.json
                            └── serialization-config.json

命令行编译

在完成上一步后重新打包 jar

使用 native-image -jar 编译 jar

native-image -jar demo.jar   

编译完成就可以得到一个可运行的二进制文件了

构建工具编译

gradle

官方文档: https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html

此处使用 KotlinDSL

首先添加插件

plugins {
    id("org.graalvm.buildtools.native") version "0.10.1"
}

添加配置

graalvmNative {
    binaries {
        named("main") {
            imageName.set("demo")
            mainClass.set("org.example.demo.Main")
            useFatJar.set(true)
        }
    }
    binaries.all {
        buildArgs.add("--verbose")
    }
}

添加完成之后就可以使用 gradle nativeCompile 编译, 并且可以使用 gradle nativeRun 直接运行

maven

与 gradle 类似

官方文档: https://graalvm.github.io/native-build-tools/latest/maven-plugin.html

注意事项

  • 编译运行后提示 "ClassNotFound" 一般是反射配置问题
  • 编译运行后提示 "UnsupportedFeature" 可能是你使用了某些不兼容的库, 比如 log4j2
  • 社区版为 Serial GC, 需要细心配置 vm 选项