This episode covers native images in Quarkus: compilation with GraalVM or Mandrel, build-time augmentation and AOT compilation, startup time and memory footprint optimization, as well as debugging native images and compatibility tradeoffs.

In episode 1 you heard Quarkus' promise: tens of milliseconds of startup and a small memory footprint. That promise is fulfilled through native images — ahead-of-time compilation that turns a Java application into a self-contained executable without a JVM.
Episode 16 covers native images thoroughly: compilation with GraalVM or Mandrel, build-time augmentation and AOT compilation, startup and memory optimization, as well as debugging native images and the compatibility tradeoffs you need to understand.
GraalVM is a JDK with an ahead-of-time compiler. Mandrel is a GraalVM distribution optimized for Quarkus by Red Hat. Both can compile an application into a native executable.
Make sure GraalVM is installed, or use a container build:
./mvnw package -Pnative
./mvnw package -Pnative -Dquarkus.native.container-build=trueThe flag -Dquarkus.native.container-build=true runs the compilation inside a container, so you don't need to install GraalVM locally — just have Docker running.
After the build, run the executable directly:
./target/belajar-quarkus-1.0.0-SNAPSHOT-runnerThere's no java -jar — the -runner file is a self-contained executable. The command curl http://localhost:8080/hello shows the application responding almost instantly.
The key is the combination of build-time augmentation (episode 2) and AOT compilation. Quarkus processes metadata in the build phase, then GraalVM compiles the application with static context: all used classes are already known, reflection is explicitly allowed, and unused code is discarded.
Configuration that involves build-time values (like quarkus.native.*) is processed at compilation time, not runtime. That's why some properties can't be changed via environment variables — they're already frozen into the executable.
A typical comparison:
JVM mode : 0.7-1.2 seconds, memory 150-300MB
Native image : 0.02-0.08 seconds, memory 50-80MBNative images are perfect for serverless and Kubernetes autoscaling, where pods are created and destroyed frequently.
A few configuration options:
quarkus.native.remove-unused-symbols=true
quarkus.native.enable-fallback-images=false
quarkus.native.strip-debug-symbols=true
quarkus.native.gc=epsilonremove-unused-symbols: discards unused symbols.gc=epsilon: a garbage collector without deallocation — suited for short workloads with minimal latency.Measure and verify the optimization:
time ./target/belajar-quarkus-1.0.0-SNAPSHOT-runner &
curl -s http://localhost:8080/q/healthtime measures execution duration. Compare JVM vs native for concrete data before setting performance targets.
Native images don't come for free:
@RegisterForReflection or configuration.import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection
public class DtoPesan {
public String isi;
public long timestamp;
}@RegisterForReflection tells GraalVM to keep reflection metadata for DtoPesan in the native image. Always audit code that uses reflection before building native.
JVM mode remains available for debugging; use native in production. For native-specific issues, run with detailed reports:
quarkus.native.enable-reports=truequarkus.native.enable-reports=true produces detailed reports about the features GraalVM uses — useful when an error only appears in native.
Episode 16 gives you Quarkus' ultimate weapon: understanding native images with GraalVM and Mandrel, how build-time augmentation and AOT work together, startup, memory, and size optimization strategies, as well as the tradeoffs and debugging approaches you need to know before deploying native in production.
Key takeaways:
-Dquarkus.native.container-build=true builds native without a local GraalVM.@RegisterForReflection.quarkus.native.enable-reports=true helps with native-specific debugging.In episode 17 we'll cover testing and quality assurance — unit testing with JUnit 5 and the Quarkus Test Framework, integration testing with @QuarkusTest, mocking and REST client testing, as well as Testcontainers and end-to-end testing.