Oracle GraalVM 只是拿不到下新补丁,主版本免费用;而 GraalVM Community Edition (CE) 缺失 G1 垃圾回收的支持,只用于洁癖者使用。
Substrate VM(svm.jar/5MiB)相比 HotSpot VM,精简掉了JIT 等运行时组件,内存占用从约 100MB 减少为不到 10MB。
全量包含(JDK模块除外):-H:Preserve=module=ALL-UNNAMED
native-image.properties 配置 Args = --features=pkg.NativeFeature
原理:将jar包和classes转译为二进制程序,但Resource文件则需要显式包含进来,且不会自动包含jar包内资源。访问资源用 Thread.currentThread().getContextClassLoader().getResourceAsStream();
输出 jar 包内配置选项最终组合:native-image --verbose -jar build/x-0.1-all.jar
反射配置:
如果 pkg.Cls$InnerCls 非 static 还需配置下 pkg.Cls;Java Record 天生就是 static。
[
{
"name": "pkg.Cls",
"queryAllDeclaredConstructors": true,
"queryAllDeclaredMethods": true,
"queryAllDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"allDeclaredFields": true,
"allDeclaredClasses": true,
"allRecordComponents": true
}
]
Jetty库Graal配置 - https://github.com/oracle/graalvm-reachability-metadata/tree/master/metadata/org.eclipse.jetty/jetty-server
Jetty 运行时似乎用到了 jar 包内的 *.xsd 文件,故不能排除 - "excludes": [{ "pattern": "^jakarta/servlet/resources/.*\\.xsd$" }]
插件用法:
plugins {
application
// ./gradlew nativeCompile 会识别 application.mainClass 作为程序入口
// 输出目录 ls graalvm-app/build/native/nativeCompile/[项目名/Windows则含后缀.exe]
// 部分依赖库(Jetty等)还会生成 libmanagement_ext.so 库,应跟主程序(约17MB)放同目录下待用,未测是否必须部署!
// 执行(亲测GCE对其SSE完美支持) chmod 777 graalvm-app & ./graalvm-app
id("org.graalvm.buildtools.native") version "1.1.2"
}
graalvmNative {
// 已存在 GRAALVM_HOME 应关闭探测;不支持变种 Mandrel。
toolchainDetection.set(true)
// 自动匹配官方维护的反射元数据配置
//metadataRepository { enabled.set(false) }
// src/main/resources/META-INF/native-image/主程序不重名即可/但尽量两层目录起步/reflect-config.json,同时合并全格式的 reachability-metadata.json。
binaries {
named("main") {
verbose.set(true)
fallback.set(false) // 等同 --no-fallback
//buildArgs.add("-march=native") // 充分利用 CPU SIMD 等特性;或兼容旧处理器 compatibility
}
}
agent {
metadataCopy {
inputTaskNames.add("run") // ./gradlew metadataCopy --task run、--task test
outputDirectories.add("src/main/resources/META-INF/native-image/my-main-website/censored-web/") // or --dir target-dir/
mergeWithExisting.set(true)
}
}
}
dependencies { // 该插件似乎依赖以下俩库,否则报警告。
implementation("com.google.guava:guava:33.6.0-jre")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.1.0")
//implementation(platform("org.eclipse.jetty:jetty-bom:12.1.10")) // 注释避开 listLibrariesMissingMetadata 任务扫描;合并配置路径 - graalvm-app\build\reports\native\list-libraries-missing-metadata.json
}
注解声明可达性:
// compileOnly("org.graalvm.sdk:nativeimage:25.0.3") // ImageInfo.inImageCode() 找不到类可换为 System.getProperty("org.graalvm.nativeimage.imagecode") 返回 buildtime、runtime、agent
// 注解扫描太耗时(已废弃),故显式将 Args = --features=pkg.NativeFeature 写入 src\main\resources\META-INF\native-image\my-main-website\censored-web\native-image.properties
public class NativeFeature implements Feature {
@Override public void beforeAnalysis(BeforeAnalysisAccess access) {
System.out.println("NativeFeature beforeAnalysis...");
RuntimeReflection.register(String.class);
}
}
容器中运行:
首选包含 C/C++ 运行环境的基础镜像进行构建(免登陆) gcr.io/distroless/cc
只有纯 Go 程序(自带DNS方案)才使用 distroless/static 或包含了 glibc 但无 libstdc++.so 的 gcr.io/distroless/base