You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3 + GraalVM 23.x 构建的AWS Lambda原生镜像启动时加载本地库触发空指针异常

Spring Boot 3 + GraalVM 23.x 构建的AWS Lambda原生镜像启动时加载本地库触发空指针异常

我最近用Spring Boot 3和GraalVM 23.x构建AWS Lambda的原生镜像,镜像本身构建成功了,但每次尝试调用REST方法时,都会触发启动错误,错误类型是Runtime.ExitError,核心是空指针异常,具体日志如下:

2024-12-30T20:19:53.908Z INIT_REPORT Init Duration: 675.51 ms Phase: init Status: error Error Type: Runtime.ExitError
2024-12-30T20:19:53.905Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNILibraryInitializer.callOnLoadFunction(JNILibraryInitializer.java:70)
2024-12-30T20:19:53.905Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNILibraryInitializer.initialize(JNILibraryInitializer.java:131)
2024-12-30T20:19:53.905Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.NativeLibrarySupport.addLibrary(NativeLibrarySupport.java:128)
2024-12-30T20:19:53.905Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.NativeLibrarySupport.addLibrary(NativeLibrarySupport.java:84)
2024-12-30T20:19:53.905Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.NativeLibraries.loadLibrary0(NativeLibraries.java:147)
2024-12-30T20:19:53.905Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.NativeLibraries.loadLibraryAbsolute(NativeLibraries.java:102)
2024-12-30T20:19:53.905Z at java.base@23.0.1/java.lang.ClassLoader.loadLibrary(ClassLoader.java:116)
2024-12-30T20:19:53.905Z at java.base@23.0.1/java.lang.Runtime.load0(Runtime.java:852)
2024-12-30T20:19:53.905Z at java.base@23.0.1/java.lang.System.load(System.java:2028)
2024-12-30T20:19:53.905Z at com.amazonaws.services.lambda.runtime.api.client.runtimeapi.JniHelper.unpackAndLoad(JniHelper.java:51)
2024-12-30T20:19:53.905Z at com.amazonaws.services.lambda.runtime.api.client.runtimeapi.JniHelper.load(JniHelper.java:38)
2024-12-30T20:19:53.905Z at com.amazonaws.services.lambda.runtime.api.client.runtimeapi.NativeClient.init(NativeClient.java:16)
2024-12-30T20:19:53.905Z at com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClientImpl.<init>(LambdaRuntimeApiClientImpl.java:41)
2024-12-30T20:19:53.905Z at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:203)
2024-12-30T20:19:53.905Z at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:190)
2024-12-30T20:19:53.905Z ... 1 more
2024-12-30T20:19:53.901Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions$Support.getFieldID(JNIFunctions.java:1858)
2024-12-30T20:19:53.901Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions.GetFieldID(JNIFunctions.java:461)
2024-12-30T20:19:53.901Z at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIOnLoadFunctionPointer.invoke(JNILibraryInitializer.java)
2024-12-30T20:19:53.900Z Caused by: java.lang.NullPointerException
2024-12-30T20:19:53.898Z at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:192)
2024-12-30T20:19:53.898Z at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.main(AWSLambda.java:180)
2024-12-30T20:19:53.894Z Exception in thread "main" java.lang.Error: java.lang.NullPointerException
2024-12-30T20:19:53.319Z LOGS Name: cloudwatch_lambda_agent State: Subscribed Types: [Platform]

在构建镜像前,我已经运行了Native Agent来生成元数据:

c:\projects\graalvm-jdk-23.0.1+11.1\bin\java.exe -agentlib:native-image-agent=config-output-dir=META-INF/native-image -jar kount-worker.jar

并且在构建时使用了生成的reachability-metadata.json文件。

我的Maven配置如下:

<build>
    <finalName>kount-worker</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.4.1</version>
            <configuration>
                <jvmArguments>--enable-preview</jvmArguments>
                <compilerArguments>--enable-preview</compilerArguments>
            </configuration>
            <executions>
                <execution>
                    <id>process-aot</id>
                    <goals>
                        <goal>process-aot</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <!--            <plugin>-->
        <!--                <groupId>org.springframework.boot</groupId>-->
        <!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
        <!--                <configuration>-->
        <!--                    <excludes>-->
        <!--                        <exclude>-->
        <!--                            <groupId>org.projectlombok</groupId>-->
        <!--                            <artifactId>lombok</artifactId>-->
        <!--                        </exclude>-->
        <!--                    </excludes>-->
        <!--                </configuration>-->
        <!--                <executions>-->
        <!--                    <execution>-->
        <!--                        <goals>-->
        <!--                            <goal>repackage</goal>-->
        <!--                        </goals>-->
        <!--                    </execution>-->
        <!--                </executions>-->
        <!--            </plugin>-->
        <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <extensions>true</extensions>
            <executions>
                <execution>
                    <id>build-native</id>
                    <goals>
                        <goal>compile-no-fork</goal>
                    </goals>
                    <phase>package</phase>
                </execution>
                <execution>
                    <id>test-native</id>
                    <goals>
                        <goal>test</goal>
                    </goals>
                    <phase>test</phase>
                </execution>
            </executions>
            <configuration>
<!--                <mainClass>com.eye4fraud.kount.KountApplication</mainClass>-->
                <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass>
                <imageName>kount-worker</imageName>
                <buildArgs>
                    <buildArg>-H:DynamicProxyConfigurationFiles=src/main/resources/proxies.json</buildArg>
                    <buildArg>--emit build-report</buildArg>
                    <buildArg>-Os</buildArg>
                    <buildArg>-H:-ReduceImplicitExceptionStackTraceInformation</buildArg>
                    <buildArg>-H:ResourceConfigurationFiles=src/main/resources/resource-config.json</buildArg>
                    <buildArg>-H:+ReportExceptionStackTraces</buildArg>
                    <buildArg>-H:JNIConfigurationFiles=src/main/resources/jni-config.json</buildArg>
                </buildArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

问题分析与解决方案

从日志来看,空指针发生在Lambda运行时加载JNI本地库的过程中,具体是JNIFunctions$Support.getFieldID尝试获取字段ID时返回了null,这通常是因为原生镜像构建时缺少Lambda运行时JNI相关的类、字段或方法的元数据配置,导致GraalVM没有将这些元素包含在镜像中。

以下是几个可以尝试的解决步骤:

1. 确保Native Agent完整捕获Lambda运行时的JNI调用

你当前运行Agent的方式只是启动了Spring Boot应用,但没有模拟Lambda的实际调用流程。Lambda的入口是AWSLambda类,它会初始化JNI库,这部分逻辑需要被Agent捕获。

  • 重新运行Agent时,要模拟Lambda的事件触发:比如编写一个测试类,调用你的Lambda handler方法,或者使用AWS Lambda本地测试工具(如sam local invoke)来触发应用,确保所有JNI相关的初始化逻辑都被Agent记录到元数据中。

2. 调整原生镜像构建参数

  • 在native-maven-plugin的buildArgs中添加-H:+JNI参数,明确开启JNI支持(虽然默认可能开启,但明确指定可以避免潜在的配置问题)。
  • 检查JNIConfigurationFiles指向的jni-config.json,确保其中包含com.amazonaws.services.lambda.runtime.api.client包下的JNI相关配置,比如JniHelper、NativeClient类的字段和方法。如果缺失,可以手动添加对应的条目,或者重新运行Agent生成完整的配置。

3. 检查Spring Boot与Lambda的原生支持配置

Spring Boot 3.x提供了对AWS Lambda的原生支持,建议使用spring-boot-starter-aws-lambda依赖,这样Spring Boot会自动处理Lambda的入口类和原生镜像配置,避免手动指定AWSLambda作为main类可能带来的配置遗漏。

  • 添加依赖后,Spring Boot会生成适配Lambda的AOT代码,确保所有必要的元数据被正确包含。

4. 暂时关闭预览特性

你在Spring Boot插件中配置了--enable-preview,预览特性可能会与GraalVM的原生镜像构建存在兼容性问题。尝试移除jvmArguments和compilerArguments中的--enable-preview配置,重新构建镜像看是否能解决问题。

5. 验证元数据文件的完整性

检查META-INF/native-image下的所有配置文件,确保reachability-metadata.json包含Lambda运行时相关的类路径,resource-config.json包含必要的资源文件,dynamic-proxy-config.json包含需要动态代理的接口。如果有缺失,可以参考GraalVM的文档手动补充。

备注:内容来源于stack exchange,提问作者avalon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:25:27