从此

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。

原理:将jar包和classes转译为二进制程序,但Resource文件则需要显式包含进来,且不会自动包含jar包内资源。访问资源用 Thread.currentThread().getContextClassLoader().getResourceAsStream();
Jetty库Graal配置 - https://github.com/oracle/graalvm-reachability-metadata/tree/master/metadata/org.eclipse.jetty/jetty-server
插件用法:
  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 则忽略其他 *.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 mydir/
            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
首选 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);

资源:
  自动包含该目录下资源 src\main\resources\META-INF\resources\
  需显式包含资源根目录 src\main\resources\
  读取资源文件亲测可用:
    Thread.currentThread().getContextClassLoader().getResourceAsStream("x.txt");
    StreamUtil.class.getResourceAsStream("/x.txt");

HTTP/HTTPS: 
  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:
  Quarkus 支持 class/record 自定义实体写成内部类,但必须注解@RegisterForReflection,且 class 必须存在无参构造器:
    @RegisterForReflection public record ResultRecord(String text) { }
    Response.ok(new ResultRecord(r)).header("X-Robots-Tag", "noindex").build();
  Quarkus 不支持 @FormParam("key") 接参,需换用 MultipartFormDataInput - input.getFormDataMap().get("k").getFirst().getBodyAsString();

其他

使 houbb:segment 分词库支持 Quarkus 原生构建使 houbb:opencc4j 中文简繁转换库支持 Quarkus 原生构建