AOT 提前编译 Ahead-of-Time .NET9+、JDK24起支持AOT、Quarkus(GraalVM)
综合/最新
编程
主要
- 通用
JDK24 支持了部分 GC 环境的AOT,自 JDK26 起支持了包含 ZGC 在内的所有 GC;JDK25起AOT记录和创建流程合并为单步 java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...。
Java
- GraalVM
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$" }] 静态代码块儿配置: StaticBlock.java static { var x = System.getenv("ENV_X"); } --initialize-at-run-time=pkg.StaticBlock 插件用法: 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- Quarkus
GraalVM native-image.properties 在构建时,关键参数会被 Quarkus 覆盖影响,故尽量只配置 Quarkus 的 application.properties。 ./gradlew build ... -Dquarkus.native.march=armv8-a 比 application.properties 写死的 quarkus.native.march=x86-64-v2 更灵活。 Quarkus 支持 class/record 自定义实体写成内部类,但必须 implements Feature 或注解 @RegisterForReflection,且 class 必须存在无参构造器: @io.quarkus.runtime.annotations.RegisterForReflection public record Resp(String x) { } Quarkus 扫描 jar 包内的类注解: org.kordamp.gradle.jandex 插件生成 META-INF/jandex.idx 或: quarkus.index-dependency..group-id= quarkus.index-dependency. .artifact-id= quarkus.index-dependency.acme.group-id=org.acme quarkus.index-dependency.acme.artifact-id=acme-api 首选 quarkus-resteasy,若未用到 Jakarta StreamingOutput 则可换用 reactive 的 quarkus-rest。 quarkus-rest 中需要用 Mutiny 库的 Multi<byte[]> 来替代 StreamingOutput;其 Uni 类型名的含义为单一性前缀。 quarkus-rest-client 兼容 quarkus-rest 和 quarkus-resteasy 服务端,JVM跑不了其依赖的ArC库CDI环境,已取代 quarkus-resteasy-client。 @RegisterRestClient(baseUri = "https://domain.name/apis") 方式或 QuarkusRestClientBuilder.newBuilder().baseUri(URI.create("https://domain.name/api")).build(ClientInterface.class); Quarkus 已内置 Agroal 连接池 @Inject AgroalDataSource ads; ads.getConnection(); // 关闭只是回到池子,并未断开连接。 手动 CDI - Arc.container().instance(MyBatisMapper.class).get(); 资源: 自动包含该目录下资源 src\main\resources\META-INF\resources\ 需显式包含资源根目录 src\main\resources\ 读取资源文件亲测可用: Thread.currentThread().getContextClassLoader().getResourceAsStream("x.txt"); StreamUtil.class.getResourceAsStream("/x.txt"); HTTP/HTTPS: 默认端口为 8080/8443。 environment variable QUARKUS_HTTP_SSL_CERTIFICATE_KEY_STORE_PASSWORD. quarkus.http.port=8080 # 关闭80端口访问: quarkus.http.insecure-requests=disabled quarkus.http.ssl-port=8443 quarkus.http.ssl.certificate.key-store-file=/path/to/keystore 非quarkus环境Rest Client: implementation("org.jboss.weld.se:weld-se-core:6.0.3.Final") implementation("io.smallrye:jandex:3.4.0") // for weld implementation("org.jboss.resteasy.microprofile:microprofile-rest-client:3.0.1.Final") implementation("io.smallrye.config:smallrye-config:3.13.4") // for resteasy implementation("io.quarkus:quarkus-undertow") // Servlet @Path("meta") @RegisterRestClient(baseUri = "https://congci.com/main/apis/core") public interface DefaultRestClient { @GET // https://congci.com/main/apis/core/meta/servlet-info @Path("/servlet-info") @Produces(MediaType.TEXT_PLAIN) public String servletInfo(); } CDI.current().select(DefaultRestClient.class).get(); 最简实例: dependencies { implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")) implementation("io.quarkus:quarkus-arc") // Build-Time CDI implementation("io.quarkus:quarkus-rest") // Vert.x 反应式 JAX-RS or 换为传统的 quarkus-resteasy } Restful: @RegisterForReflection public record Resp(String text) { } Response.ok(new Resp(r)).header("X-Robots-Tag", "noindex").build(); Quarkus 不支持 @FormParam("key") 接参,需换用 MultipartFormDataInput - input.getFormDataMap().get("k").getFirst().getBodyAsString();
其他
使 houbb:segment 分词库支持 Quarkus 原生构建、使 houbb:opencc4j 中文简繁转换库支持 Quarkus 原生构建