javamicronautgraalvmgraalvm-native-image

native image creation fails after upgrading graalvm version from 22.1.0 to 22.3.2


I'm compiling my micronaut application into a native image using the command native-image -cp build/libs/*-all.jar, which is executed within a docker image. I also supply a native-image.properties file that looks like below:

Args = -H:ResourceConfigurationFiles=build/graal/resource-config.json,build/native/generated/generateResourcesConfigFile/resource-config.json \
       -H:ReflectionConfigurationFiles=build/graal/reflect-config.json,build/resources/main/graal/reflect-config.json \
       -H:JNIConfigurationFiles=build/graal/jni-config.json \
       -H:DynamicProxyConfigurationFiles=build/graal/proxy-config.json \
       -H:PredefinedClassesConfigurationFiles=build/graal/predefined-classes-config.json \
       -H:Name=micronautgraalapp \
       --no-fallback \
       --report-unsupported-elements-at-runtime \
       --initialize-at-build-time=sun.instrument.InstrumentationImpl \
       --install-exit-handlers \
       -H:EnableURLProtocols=http,https \
       -H:+ReportExceptionStackTraces \
       -H:+StaticExecutableWithDynamicLibC \
       -J-Xmx3072m \
       -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk=ALL-UNNAMED \
       -H:Class=io.github.devatherock.ldapsearch.Application

I use the shortcut command make fast-build docker-build which runs the tests first, to generate the reflection config, and then builds the native image within docker. This works fine and creates the native image successfully with graalvm version 22.1.0. The working version of the application can be found here. After upgrading graalvm version to 22.3.2, the native image creation fails midway with the below exception:

#10 84.42 Error: Unsupported features in 4 methods
#10 84.42 Detailed message:
#10 84.42 Error: Detected a ZipFile object in the image heap. A ZipFile object contains pointers to unmanaged C memory and file descriptors, and these resources are no longer available at image runtime.  To see how this object got instantiated use --trace-object-instantiation=java.util.jar.JarFile. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
#10 84.42 Trace: Object was reached by
#10 84.42   reading field jdk.internal.module.ModuleReferences$JarModuleReader.jf of constant 
#10 84.42     jdk.internal.module.ModuleReferences$JarModuleReader@7205edd8: jdk.internal.module.ModuleReferences$JarModuleReader@7205edd8
#10 84.42   reading field java.util.concurrent.ConcurrentHashMap$Node.val of constant 
#10 84.42     java.util.concurrent.ConcurrentHashMap$Node@4d58ca3f: [module org.graalvm.nativeimage.builder, location=file:///usr/lib64/graalvm/graa...
#10 84.42   indexing into array java.util.concurrent.ConcurrentHashMap$Node[]@639968a3: [Ljava.util.concurrent.ConcurrentHashMap$Node;@639968a3
#10 84.42   reading field java.util.concurrent.ConcurrentHashMap.table of constant 
#10 84.42     java.util.concurrent.ConcurrentHashMap@60bb74c6: {[module jdk.internal.ed, location=jrt:/jdk.internal.ed]=jdk.internal.module.Sys...
#10 84.42   reading field jdk.internal.loader.BuiltinClassLoader.moduleToReader of constant 
#10 84.42     jdk.internal.loader.ClassLoaders$AppClassLoader@2cfb4a64: jdk.internal.loader.ClassLoaders$AppClassLoader@2cfb4a64
#10 84.42   reading static field java.lang.ClassLoader.scl

Setting both java.util.jar.JarFile and java.util.zip.ZipFile classes to initialize at runtime using --initialize-at-run-time also did not help - causes a different error saying that the class has already been initialized. The failing version of the code is present in a different branch


Solution

  • The way I finally solved this was by stopping the generation of reflection config using the tracing agent, -agentlib:native-image-agent, which I was previously doing and instead using micronaut's @ReflectionConfig annotation to generate reflection config for only classes that eventually resulted in exceptions at runtime.

    The reflection config file:

    
    package io.github.devatherock.ldapsearch.config;
    
    import javax.net.ssl.SSLSocketFactory;
    
    import com.sun.jndi.ldap.LdapCtxFactory;
    import io.micronaut.core.annotation.ReflectionConfig;
    import io.micronaut.core.annotation.TypeHint.AccessType;
    import net.logstash.logback.encoder.LogstashEncoder;
    
    @ReflectionConfig(type = LogstashEncoder.class, accessType = AccessType.ALL_DECLARED_CONSTRUCTORS)
    @ReflectionConfig(type = LdapCtxFactory.class, accessType = AccessType.ALL_DECLARED_CONSTRUCTORS)
    @ReflectionConfig(type = SSLSocketFactory.class, accessType = AccessType.ALL_DECLARED_METHODS)
    public class GraalConfig {
    }
    

    Updated native-image.properties:

    Args = -H:ResourceConfigurationFiles=build/native/generated/generateResourcesConfigFile/resource-config.json \
           -H:Name=micronautgraalapp \
           --no-fallback \
           --report-unsupported-elements-at-runtime \
           --initialize-at-build-time=sun.instrument.InstrumentationImpl \
           --install-exit-handlers \
           -H:EnableURLProtocols=http,https \
           -H:+ReportExceptionStackTraces \
           -H:+StaticExecutableWithDynamicLibC \
           -J-Xmx3072m \
           -H:NumberOfThreads=2 \
           -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk=ALL-UNNAMED \
           -H:Class=io.github.devatherock.ldapsearch.Application
    

    All the changes can be found in this PR.