GraalVM 21 Native Image 编译踩坑全记录:从 class 初始化冲突到成功构建

项目 版本/信息
GraalVM 21.0.12+7-LTS (Oracle GraalVM)
Spring Boot 3.4.5
native-maven-plugin 0.10.6
操作系统 Windows 25H2
C 编译器 cl.exe (Microsoft x64 19.51.36256)
[环境信息]

项目背景

Spring Boot 3.x 智能客服项目,集成了 Nacos(配置中心/服务注册)、Milvus(向量数据库)、Logback(日志)、MyBatis-Plus、Elasticsearch、Redis 等组件。配置了 Maven 双 Profile(JVM / Native)支持 GraalVM AOT 编译。


编译命令是:

bash 复制代码
mvn clean -Pnative native:compile -X

第一阶段:grpc-netty-shaded 类初始化冲突(不可调和)

错误现象

Error: Classes that should be initialized at run time got initialized during image building:

io.grpc.netty.shaded.io.netty.util.AbstractReferenceCounted was unintentionally initialized at build time.

...(27+ 个 netty 类)

根因分析

Nacos 2.x 通过 nacos-client 传递依赖 grpc-netty-shaded,该 jar 内嵌 META-INF/native-image/native-image.properties 声明了所有 netty 类为 --initialize-at-run-time。但 native-image builder 的 points-to 分析阶段会通过 ensureClassInitialized 强制在构建时初始化这些类,两者不可调和。

GraalVM 21 硬性阻止从 RUN_TIME 改为 BUILD_TIME(ClassInitializationConfiguration.insertRec() 方法),报 "Incompatible change of initialization policy"。

排查过程

最初以为是 Nacos 独有来源,排除 nacos-client 后发现 grpc-netty-shaded 仍在 classpath 上:

bash 复制代码
mvn dependency:tree -Pnative -Dincludes=io.grpc:grpc-netty-shaded

发现第二条依赖链:langchain4j-milvus → milvus-sdk-java → grpc-netty-shaded:1.59.1

最终方案

将两个冲突源的依赖 scope 设为 provided(编译可用,native-image 运行时不在 classpath):

XML 复制代码
<profile>
    <id>native</id>
    <dependencies>
        <!-- Nacos: grpc-netty-shaded via nacos-client -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Milvus: grpc-netty-shaded via milvus-sdk-java -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-milvus</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</profile>

注意 :不能简单用 <exclusions> 排除 nacos-client,因为 spring-cloud-starter-alibaba-nacos-logging 子模块会引用 nacos-clientNacosLogging 类,导致 NoClassDefFoundErrorprovided scope 是更干净的方案。

第二阶段:Logback/SLF4J 类初始化冲突(通配符陷阱)

错误现象

排除 grpc-netty-shaded 后,构建继续报错:

Error: Classes that should be initialized at run time got initialized during image building:

ch.qos.logback.core.status.StatusBase was unintentionally initialized at build time.

ch.qos.logback.classic.Logger was unintentionally initialized at build time.

org.slf4j.LoggerFactory was unintentionally initialized at build time.

org.apache.commons.logging.LogFactory was unintentionally initialized at build time.

...(共 11 个类)

踩坑 1:** 通配符不生效

首先在 pom.xmlbuildArgs 中添加:

XML 复制代码
<buildArg>--initialize-at-build-time=ch.qos.logback.**,org.slf4j.**,org.apache.commons.logging.**</buildArg>

结果:完全无效,同样的错误。

踩坑 2:* 通配符也不生效

改为单星号:

XML 复制代码
<buildArg>--initialize-at-build-time=ch.qos.logback.*,org.slf4j.*,org.apache.commons.logging.*</buildArg>

结果:同样无效。

根因:GraalVM 21 的通配符语义

GraalVM 21 的 ClassInitializationConfiguration 使用 startsWith + 边界检查的匹配逻辑。*** 在包名模式中的行为与直觉不符 ------它们并不像 Ant 风格 glob 那样工作。经过实测验证,GraalVM 21 的通配符对这些类不生效,必须使用显式类名。

最终方案:显式列出所有 11 个类

XML 复制代码
<buildArg>--initialize-at-build-time=ch.qos.logback.core.status.StatusBase,ch.qos.logback.core.status.InfoStatus,ch.qos.logback.core.util.StatusPrinter,ch.qos.logback.core.util.StatusPrinter2,ch.qos.logback.core.util.Loader,ch.qos.logback.classic.Logger,ch.qos.logback.classic.Level,org.slf4j.LoggerFactory,org.slf4j.helpers.Reporter,org.apache.commons.logging.LogFactory,org.apache.commons.logging.impl.Jdk14Logger</buildArg>

结果:类初始化错误全部消失!构建进入下一阶段。


第三阶段:JDK Console 相关类冲突(连环坑)

错误 1:ProxyingConsole 堆对象错误

Error: An object of type 'java.io.ProxyingConsole' was found in the image heap.

Trace: Object was reached by

trying to constant fold static field sun.security.util.Password$ConsoleHolder.c1

builder 分析阶段尝试常量折叠 Password$ConsoleHolder.c1 静态字段,该字段持有 ProxyingConsole 对象。但 ProxyingConsole 被标记为 RUN_TIME,不允许出现在 image heap 中。

尝试修复 :添加 --initialize-at-build-time=java.io.ProxyingConsole

错误 2:Console 继承链冲突

Error: Incompatible change of initialization policy for java.io.Console:

trying to change BUILD_TIME super type of java.io.ProxyingConsole to RUN_TIME

for JDK native code support via JNI

ProxyingConsole extends Console,而 GraalVM 21 的 JDK JNI 注册要求 java.io.Console 保持 RUN_TIME。强制 ProxyingConsole 为 BUILD_TIME 会级联要求父类 Console 也变为 BUILD_TIME,与 JDK 原生注册冲突。

结论 :不能将 ProxyingConsole 设为 BUILD_TIME,必须从源头阻止对象创建。

错误 3:级联初始化失败

改为 --initialize-at-run-time=sun.security.util.Password$ConsoleHolder 后:

Class initialization of jdk.internal.io.JdkConsoleImpl$1ConsoleHolder failed.

java.lang.IllegalStateException: Can't create a dedicated password console

since a real console already exists

Password$ConsoleHolder<clinit> 触发了 JdkConsoleImpl$1ConsoleHolder 的初始化,后者在构建环境中失败(没有可用的专用密码控制台)。

最终方案:同时标记两个 JDK 内部类为 RUN_TIME

XML 复制代码
<buildArg>--initialize-at-run-time=jdk.internal.io.JdkConsoleImpl$1ConsoleHolder,sun.security.util.Password$ConsoleHolder</buildArg>
  • Password$ConsoleHolder → RUN_TIME:阻止构建时创建 ProxyingConsole 对象
  • JdkConsoleImpl$1ConsoleHolder → RUN_TIME:阻止级联初始化崩溃

结果:构建成功通过!


最终配置总览

pom.xml native profile 关键配置

XML 复制代码
<profile>
    <id>native</id>
    <dependencies>
        <!-- grpc-netty-shaded 冲突源:provided scope 排除 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-milvus</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <version>0.10.6</version>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <id>build-native</id>
                        <goals><goal>compile</goal></goals>
                        <phase>package</phase>
                    </execution>
                    <execution>
                        <id>default-cli</id>
                        <goals><goal>compile</goal></goals>
                        <phase>package</phase>
                        <configuration>
                            <buildArgs>
                                <buildArg>--initialize-at-build-time=ch.qos.logback.core.status.StatusBase,ch.qos.logback.core.status.InfoStatus,ch.qos.logback.core.util.StatusPrinter,ch.qos.logback.core.util.StatusPrinter2,ch.qos.logback.core.util.Loader,ch.qos.logback.classic.Logger,ch.qos.logback.classic.Level,org.slf4j.LoggerFactory,org.slf4j.helpers.Reporter,org.apache.commons.logging.LogFactory,org.apache.commons.logging.impl.Jdk14Logger</buildArg>
                                <buildArg>--initialize-at-run-time=jdk.internal.io.JdkConsoleImpl$1ConsoleHolder,sun.security.util.Password$ConsoleHolder</buildArg>
                            </buildArgs>
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <buildArgs>
                        <buildArg>--initialize-at-build-time=ch.qos.logback.core.status.StatusBase,ch.qos.logback.core.status.InfoStatus,ch.qos.logback.core.util.StatusPrinter,ch.qos.logback.core.util.StatusPrinter2,ch.qos.logback.core.util.Loader,ch.qos.logback.classic.Logger,ch.qos.logback.classic.Level,org.slf4j.LoggerFactory,org.slf4j.helpers.Reporter,org.apache.commons.logging.LogFactory,org.apache.commons.logging.impl.Jdk14Logger</buildArg>
                        <buildArg>--initialize-at-run-time=jdk.internal.io.JdkConsoleImpl$1ConsoleHolder,sun.security.util.Password$ConsoleHolder</buildArg>
                    </buildArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</profile>

native-image.properties

java 复制代码
Args = -H:IncludeResources=.*\\.xml$|.*\\.yml$|.*\\.properties$|.*\\.md$|.*\\.sql$ \
       --no-fallback \
       -H:+ReportExceptionStackTraces

踩坑经验总结

# 教训
1 grpc-netty-shaded 的 RUN_TIME 声明不可覆盖 GraalVM 21 硬性阻止 RUN_TIME → BUILD_TIME 的策略变更,唯一方案是排除依赖
2 ** 通配符在 GraalVM 21 中不生效 ClassInitializationConfiguration 使用 startsWith 匹配,** 被当作字面字符
3 * 通配符同样不生效 单星号也不匹配子包中的类,必须使用显式类名
4 --initialize-at-build-time=ProxyingConsole 与 JDK JNI 冲突 ProxyingConsole extends Console,父类 Console 被 JDK 原生注册锁定为 RUN_TIME
5 只标记 Password$ConsoleHolder 为 RUN_TIME 会级联崩溃 <clinit> 触发 JdkConsoleImpl$1ConsoleHolder 初始化,必须两者同时标记
6 XML 注释中不能包含 -- pom.xml 的 XML 注释中写 --initialize-at-run-time 会导致解析错误
7 provided scope 优于 <exclusions> 排除传递依赖的子模块可能引用被排除 jar 的类,provided 更干净

Native 模式限制

功能 JVM 模式 Native 模式
Nacos 配置中心/服务注册 ❌ (使用本地 application.yml)
Milvus 向量存储 ❌ (provided scope 排除)
其他所有功能

希望这篇记录能帮助遇到类似问题的同学。GraalVM Native Image 的编译错误排查核心思路:仔细阅读错误
信息中的 trace 和 suggestion
,它们通常已经给出了精确的修复方向

相关推荐
tachibana232 分钟前
复杂任务怎么做的任务拆分?
人工智能·ai·大模型·llm·agent
tachibana235 分钟前
ReAct、Plan-and-Execute、Reflection 三种范式有什么核心区别
人工智能·ai·大模型·llm·agent
尘中远1 小时前
7大开源Agent源码对比解读——性能设计
ai·开源·agent·codex·deepseek·harness
VIP_CQCRE1 小时前
用 Ace Data Cloud 开启 AI 能力商业化:推广平台,或打造自己的白标 AI 平台
ai·api·mcp·ace data cloud·白标平台
TechEdu2026061 小时前
[人工智能]豆包(Doubao):模型能力、应用架构与工程实践
人工智能·ai
GHL2842710902 小时前
用codex做一个简单技能学习
学习·ai
小新科研测评2 小时前
2026 年 3 种 PDF 文献翻译方案横评:公式/表格/双栏排版保真度实测
人工智能·ai·pdf·自动翻译
笨蛋©3 小时前
2026年制造业数字化:如何通过 Infra CONVERT 中国代理 实现检验计划自动化
ai·数字化·cad·质量管理·制造业