All GraalVM templates

GraalVM Native Image Rules

Native-image build, reflection config, resource inclusion, and runtime initialization.

DevZone Tools1,620 copiesUpdated Apr 18, 2026GraalVMJava
# CLAUDE.md — GraalVM Native Image

## What native image gives you

- Fast startup (10–100ms vs 1–5s on JVM).
- Lower memory (often 1/3 to 1/5 of JVM).
- A single self-contained binary — no JRE on the target.

## What it costs you

- Slow build (1–5 minutes).
- No reflection except what's registered ahead of time.
- No dynamic class loading at runtime.
- Closed-world assumption: every code path must be reachable from `main`.

Use it for serverless, CLIs, scale-to-zero. Avoid it for throughput-bound long-running services where JIT eventually wins.

## Distributions

- **GraalVM Community Edition (CE)** — open source, MIT.
- **Mandrel** — Red Hat's downstream of GraalVM CE, focused on native-image; pairs well with Quarkus.
- **Oracle GraalVM (formerly Enterprise)** — adds JIT optimizations (G1 GC, advanced compiler). Free for production now.

## Build basics

- Maven: `org.graalvm.buildtools:native-maven-plugin`.
- Gradle: `org.graalvm.buildtools.native` plugin.
- Run: `mvn -Pnative package` or `gradle nativeCompile`.
- For a Docker build: `mvn -Pnative -Dquarkus.native.container-build=true` (Quarkus) or use the official `gcr.io/distroless/static`.

## Reflection config

Native image needs to know at build time which classes use reflection. Two ways:

1. **Tracing agent (preferred)** — run your app on the JVM with `-agentlib:native-image-agent=config-output-dir=...`, then bundle the generated config:
   ```sh
   java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/ -jar app.jar
   ```
2. **Manual hints** — add `reflect-config.json`, `resource-config.json` files in `META-INF/native-image/`.

## Resources

- Files accessed via `getResource` / `getResourceAsStream` need to be in `resource-config.json`:
  ```json
  { "resources": [{ "pattern": "config/.*\\.properties" }] }
  ```
- Or use the `@RegisterResource` annotation if your framework supports it.

## Class initialization

- Default policy: classes initialize at **runtime**.
- Some classes need to initialize at **build time** for performance — frameworks declare this via `--initialize-at-build-time=...`.
- Framework integrations (Spring, Quarkus, Micronaut) handle this for you. Custom code rarely needs to opt in.

## Image size

- Strip unused code: `--no-fallback`, `--gc=epsilon` (only for short-lived workloads), `-H:+RemoveUnusedSymbols`.
- Compress the binary: `--enable-monitoring=heapdump,jfr` only when needed (each adds size).
- For Docker, use `gcr.io/distroless/static` (~2 MB) as the runtime base.

## Debugging

- Build with `-g` for debug symbols: `--debug-info`.
- Profile with **JFR** (`-XX:+FlightRecorder` works on native).
- For deep diagnosis, run on the JVM and reproduce — native errors are often "this didn't work in JVM either, you just got lucky".

## Don't

- Don't expect every Java library to work in native. Test the integration in native mode early.
- Don't skip the tracing agent run — manual reflection config is tedious and error-prone.
- Don't disable build-time initialization to "fix" a class init bug. Find the actual cause.
- Don't ship `--report-unsupported-elements-at-runtime` to production. It hides bugs.

Other GraalVM templates