
Native Image
============


Status
------

2026-02-08: Draft
2026-02-08: Accepted
2026-03-01: Updated
2026-04-21: Updated
2026-09-14: Reviewed



Context
-------

GraalVM can compile code ahead-of-time (AOT) to generate a native binary which features:

- no JVM required at runtime
- fast startup
- low memory footprint

The AOT compilation process contain two steps that require some effort:

- reachability metadata
- class initialization


### Reachability metadata


Java allows dynamic access to code, like reflection and resource loading. Since they are not visible to static analysis, reachability metadata explicitly declares they will be used at run time.


### Class initialization

GraalVM Native Image splits class initialization into build time and run time. Safe class initializer are executed at build time, resulting in faster startup and smaller runtime footprint. However, in case a class cannot be safely executed at build time or at run time, it requires human review.



Considerations
--------------

Alternatives to Native Image are the old ways, either a .jar with dependencies at classpath, or a shadow/uber jar.



Decision
--------

It depends on the requirements:

Use native image when:
- startup time must be less than 100ms
- application must a native executable

Otherwise, use a `.jar` artifact.



Justification
-------------

With pre-built (or community provided) reachability metadata, some libraries can be used seamlessly with native image.

However, those that aren't adds overhead to the development of new systems.

Also, the build time is a big cost to the application development and test.



Previous updates
----------------


### Update 2026-04-21

As of today, it was possible to generate a native image without all the previous effort, using the native-maven-plugin and agent tracing.

#### static

According to [compatibility](https://www.graalvm.org/latest/reference-manual/native-image/metadata/Compatibility/), "--libc=<value>: musl is not supported."

When trying to build `--static` with `--libc=glibc`, the following error is presented:

    Error: Invalid option '--static'. Building static executable images is only supported with musl libc. Remove the '--static' option or add the '--libc=musl' option..

Therefore, *static* native image won't work on arm64.

#### terraform + spot instances

Using aws spot instances reduces the cost of running an arm64 by around 60%. Along with terraform, it's possible to use a temporary instance to largely reduce the native image compilation time to about 7min.


### Update 2026-03-01

As of today, it's possible to generate a native image using slf4j-simple, instead of log4j2, with the following command as example:

    native-image --no-fallback --enable-http -o launcher -cp launcher.jar:lib \
-Dorg.slf4j.simpleLogger.logFile=System.out \
--initialize-at-build-time='org.slf4j.simple.SimpleLogger,io.micronaut.jaxrs.common.CacheControlDelegate,org.slf4j.simple.SimpleLoggerConfiguration,org.slf4j.simple.OutputChoice,org.slf4j.simple.OutputChoice$OutputChoiceType,io.micronaut.jaxrs.common.EntityTagDelegate' \
--initialize-at-run-time='io.netty.handler.pcap.PcapWriteHandler$WildcardAddressHolder,io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler' \
dev.suguiura.services.dockerlogplugin.Main

However, building native image on arm64 takes about 25min, making it unfeasible for development.


