Heuristic:Apache Kafka JVM GC Tuning Defaults
| Knowledge Sources | |
|---|---|
| Domains | Optimization, JVM |
| Last Updated | 2026-02-09 12:00 GMT |
Overview
Default JVM garbage collection tuning for Kafka brokers: G1GC with 20ms pause target, 35% initiating heap occupancy, and 1GB heap.
Description
Apache Kafka ships with carefully chosen JVM garbage collection defaults optimized for broker workloads. The defaults use the G1 garbage collector with a 20-millisecond max GC pause target, an initiating heap occupancy of 35% (triggering concurrent marking earlier than the JVM default of 45%), and explicit GC invocations routed through the concurrent collector rather than triggering stop-the-world pauses. For tools and CLI utilities, the default heap is 256MB; for brokers, it is 1GB (both min and max). A `MaxInlineLevel=15` flag is included for backward compatibility with JDKs older than 14 (it became the JDK default starting in JDK 14).
Usage
Apply these defaults as a starting point for production Kafka deployments. Override `KAFKA_HEAP_OPTS` and `KAFKA_JVM_PERFORMANCE_OPTS` environment variables to tune for your specific hardware and workload. These defaults are critical for latency-sensitive broker operations where GC pauses directly impact tail latency.
The Insight (Rule of Thumb)
- Action: Use the default JVM performance options or tune from them as a baseline.
- Value: `-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -XX:MaxInlineLevel=15`
- Broker Heap: `-Xmx1G -Xms1G` (set both min and max equal to avoid heap resizing overhead).
- Tool Heap: `-Xmx256M` (lower for CLI tools that exit quickly).
- Trade-off: Lower `MaxGCPauseMillis` (20ms) prioritizes latency over throughput. Lower `InitiatingHeapOccupancyPercent` (35%) triggers GC earlier, using more CPU but preventing long pauses.
- Legacy Note: `MaxInlineLevel=15` can be removed once JDK 13 and earlier are no longer supported.
Reasoning
Kafka brokers are latency-sensitive: long GC pauses cause request timeouts, producer retries, and consumer rebalances. The G1GC collector provides predictable pause times. Setting `InitiatingHeapOccupancyPercent=35` (vs JVM default of 45%) triggers concurrent marking sooner, preventing the heap from filling up and forcing expensive full GC pauses. `ExplicitGCInvokesConcurrent` ensures that any `System.gc()` call (from libraries or NIO direct memory management) uses the concurrent collector rather than a stop-the-world pause. Setting min and max heap equal (`-Xms1G -Xmx1G`) eliminates heap resizing overhead.
Code evidence from `bin/kafka-run-class.sh:282-285`:
# JVM performance options
# MaxInlineLevel=15 is the default since JDK 14 and can be removed once older JDKs are no longer supported
if [ -z "$KAFKA_JVM_PERFORMANCE_OPTS" ]; then
KAFKA_JVM_PERFORMANCE_OPTS="-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -XX:MaxInlineLevel=15"
fi
Broker heap from `bin/kafka-server-start.sh:28-29`:
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi
Tool heap from `bin/kafka-run-class.sh:278-280`:
if [ -z "$KAFKA_HEAP_OPTS" ]; then
KAFKA_HEAP_OPTS="-Xmx256M"
fi