What garbage collector does a GraalVM native image use by default, and why does that choice fit the native use case?
answer
- Native default = Serial GC
- single-threaded, stop-the-world, smallest footprint
- G1 = Oracle GraalVM, --gc=G1, more RAM, more throughput
- Epsilon = no-op, only ultra short-lived
- trap: NOT G1 by default
basics
~20 sBy default a native image uses the Serial GC — a simple, single-threaded, stop-the-world collector. It has the lowest memory overhead, which matches native's goal of a small footprint, though it can limit throughput and cause longer pauses under heavy load.
solid answer
~40 sThe default GC in GraalVM native image is **Serial GC**: single-threaded, generational, fully stop-the-world. It's chosen because it has the smallest memory and code footprint, aligning with native's core value proposition — low RSS and fast startup — and it works well for small heaps and short-lived or lightly-loaded processes (serverless, CLIs). Its downsides are lower throughput and longer stop-the-world pauses under allocation-heavy or large-heap workloads, since collection isn't parallel or concurrent. For throughput-sensitive services, **Oracle GraalVM** offers **G1** (`--gc=G1`) at the cost of more memory, and **Epsilon** (a no-op collector) exists for ultra-short-lived tasks that never need to reclaim memory. So the default optimizes for footprint; you opt into G1 when you need throughput.
code
kotlin · 9 lines// build.gradle.kts — opt into G1 for a throughput-oriented native service (Oracle GraalVM)
graalvmNative {
binaries {
named("main") {
buildArgs.add("--gc=G1") // default without this is Serial GC
// Epsilon (no-op GC) for ultra short-lived tasks: buildArgs.add("--gc=epsilon")
}
}
}go deeper
Know the default is Serial GC — simple and low-memory.
Explain the footprint-vs-throughput trade and name G1/Epsilon alternatives.
Tie GC choice to workload and to the Oracle-vs-Community edition constraint.
Fold GC/edition choices into capacity planning and licensing/cost decisions for the fleet.
**What a GC does.** A garbage collector reclaims heap memory no longer referenced. Collectors trade off three things: memory footprint, throughput (useful work vs GC time), and pause times (how long the app stops for GC). **Serial GC — the native default.** GraalVM native image defaults to **Serial GC**: a *generational* collector (young + old generations) that is *single-threaded* and *fully stop-the-world* (the whole application pauses during a collection). It is deliberately the default because it has the **smallest footprint** — minimal collector code and metadata, small heaps — which reinforces native's headline benefits of low RSS and fast startup. For small heaps, short-lived processes (a serverless invocation), or lightly-loaded services, Serial GC is entirely adequate and keeps memory tiny. **Its limits.** Because collection is single-threaded and stop-the-world, under **allocation-heavy** load or with **large heaps**, Serial GC delivers lower throughput and longer pauses than parallel/concurrent collectors. This compounds the peak-throughput trade-off native already carries. **Alternatives.** - **G1 (`--gc=G1`)** — available in **Oracle GraalVM** (not Community Edition). A parallel, partly-concurrent, region-based collector that scales to larger heaps with better throughput and shorter pauses, at the cost of higher memory overhead. Choose it for long-running, throughput-oriented native services. - **Epsilon (`--gc=epsilon`)** — a *no-op* collector that allocates but never reclaims. Only viable for very short-lived tasks whose total allocation fits in the heap (e.g., a function that runs, produces output, and exits). Using it for a long-running service will exhaust the heap. **How to set it.** Pass a build argument via the Native Build Tools, e.g. `buildArgs.add("--gc=G1")` in the `graalvmNative` block, or `-H:+UseSerialGC` style flags. Heap sizing (`-Xmx`, or `-XX:MaximumHeapSizePercent`) still applies at runtime. **Gotcha / interview trap.** Candidates often assume native images default to G1 (the modern OpenJDK default) — they do **not**; the native default is **Serial GC**, and G1 for native is an **Oracle GraalVM** feature. Getting the edition dependency right signals real experience.
- Is G1 available in GraalVM Community Edition for native image?No. G1 for native image is an Oracle GraalVM feature. Community Edition native images are limited to Serial GC (and Epsilon), which is a real factor when planning throughput-heavy native workloads.
- When would Epsilon (no-op GC) be safe?Only for very short-lived processes whose total allocation fits within the configured heap and that exit before running out of memory — e.g., a one-shot batch task or function invocation. Never for a long-running server.
saying these in an interview costs you the question
- Saying native defaults to G1 like modern OpenJDK
- Assuming G1 is free in Community Edition
- Treating Epsilon as usable for long-running services