JVM-Heap-Tuning: Praxisleitfaden ohne Rateversuche
AI generated
_doc
_index
Elasticsearch · OpenSearch · JVM-Heap-Tuning · Garbage Collection
JVM-Heap-Tuning: Praxisleitfaden ohne Rateversuche
die 50-Prozent-Regel, das 32-GB-Limit und der Lucene-Dateicache

JVM-Heap-Tuning in Elasticsearch ist keine Frage von "je mehr Heap, desto besser", sondern eine Balance zwischen dem JVM-Heap fuer Java-Objekte und dem Betriebssystem-Dateicache, den Lucene fuer Segmentzugriffe nutzt. Wer den Heap zu gross oder ohne Ruecksicht auf die 32-Gigabyte-Grenze fuer Compressed Oops setzt, verschenkt Performance und riskiert lange Garbage-Collection-Pausen genau bei hoher Last.

18 Min. Lesezeit Heap-Sizing · Compressed Oops · G1GC · Dateicache Elasticsearch 8.x · OpenSearch 2.x

1. Warum JVM-Heap-Tuning kein Rateversuch sein sollte

Elasticsearch laeuft auf der JVM und erbt damit alle Eigenheiten von Java-Speicherverwaltung, inklusive Garbage Collection. Der haeufigste Fehler beim JVM-Heap-Tuning ist die Annahme, ein groesserer Heap sei automatisch besser: mehr Heap bedeutet mehr Speicher fuer Objekte, aber auch laengere Garbage-Collection-Zyklen, weil der Collector mehr Speicher durchsuchen muss, um nicht mehr referenzierte Objekte zu finden. Ab einer bestimmten Groesse kippt der Effekt und der Cluster wird durch GC-Pausen langsamer statt schneller.

Ein zweites, ebenso verbreitetes Missverstaendnis: Elasticsearch braucht nicht den gesamten verfuegbaren RAM als Heap. Lucene, der Suchindex-Unterbau, greift ueber Memory-Mapped-Files direkt auf den Betriebssystem-Dateicache zu, ohne den JVM-Heap zu belasten. JVM-Heap-Tuning bedeutet deshalb, bewusst Speicher zwischen Heap und Dateicache aufzuteilen, statt reflexartig den kompletten RAM dem Heap zuzuweisen.

Die zwei zentralen Faustregeln fuer JVM-Heap-Tuning, auf die dieser Beitrag im Detail eingeht, lauten: maximal fuenfzig Prozent des verfuegbaren RAM als Heap, und niemals ueber die Grenze von etwa zweiunddreissig Gigabyte hinausgehen, an der die JVM-Optimierung Compressed Oops wegfaellt. Beide Regeln zusammen verhindern die haeufigsten Fehlkonfigurationen in Produktionsclustern.

2. Die 50-Prozent-Regel: Heap versus Betriebssystem

Die zentrale Faustregel fuer JVM-Heap-Tuning lautet, maximal fuenfzig Prozent des auf einem Data-Node verfuegbaren RAM dem JVM-Heap zuzuweisen. Der Rest bleibt dem Betriebssystem fuer den Dateicache, in dem Lucene-Segmentdateien vorgehalten werden, sowie fuer andere Prozesse und den Netzwerkstack. Auf einem Node mit vierundsechzig Gigabyte RAM bedeutet das einen Heap von maximal zweiunddreissig Gigabyte, nicht mehr.

Diese Aufteilung wirkt zunaechst kontraintuitiv, weil ungenutzter Heap-Speicher vermeintlich verschenkt wird. Tatsaechlich ist der Dateicache fuer die Lesegeschwindigkeit von Suchanfragen mindestens genauso wichtig wie der Heap: Lucene greift ueber mmap auf Indexsegmente zu, und je mehr davon im Betriebssystem-Cache liegt, desto weniger Disk-I/O ist fuer eine Suchanfrage noetig. Ein zu grosser Heap auf Kosten des Dateicaches fuehrt paradoxerweise zu langsameren Suchanfragen, obwohl scheinbar mehr Speicher zur Verfuegung steht.

3. Das 32-GB-Compressed-Oops-Limit

Die JVM referenziert Java-Objekte normalerweise ueber 64-Bit-Pointer. Bei Heaps unter etwa zweiunddreissig Gigabyte aktiviert die JVM automatisch Compressed Oops, eine Optimierung, die Objektreferenzen auf 32 Bit komprimiert und dadurch Speicher spart sowie die CPU-Cache-Effizienz verbessert. Ueberschreitet der Heap diese Schwelle, schaltet die JVM automatisch auf unkomprimierte 64-Bit-Pointer um, was den effektiven Speicherbedarf pro Objekt erhoeht.

Das paradoxe Ergebnis: ein Heap von vierzig Gigabyte kann in der Praxis weniger nutzbaren Speicher fuer Objekte bereitstellen als ein Heap von einunddreissig Gigabyte mit aktivierten Compressed Oops, weil der Pointer-Overhead den vermeintlichen Groessengewinn auffrisst. Fuer JVM-Heap-Tuning bedeutet das: die Zone zwischen zweiunddreissig und etwa achtundvierzig Gigabyte Heap ist praktisch immer eine schlechtere Wahl als ein Heap knapp unter der Schwelle. Die exakte Grenze variiert leicht je nach JVM-Version, liegt aber typischerweise zwischen einunddreissig und zweiunddreissig Gigabyte.


# jvm.options.d/heap.options
# Rule 1: never exceed roughly 32 GB, compressed oops get disabled above that
# Rule 2: never exceed 50 percent of available system RAM

# Example for a data node with 64 GB total RAM
-Xms31g
-Xmx31g

# Xms and Xmx must always match, prevents heap resizing pauses at runtime

4. Off-Heap: der Lucene-Dateicache

Der Lucene-Dateicache ist kein separat konfigurierbarer Cache im Elasticsearch-Sinne, sondern schlicht der Betriebssystem-Seitencache, der automatisch kuerzlich gelesene Dateien im freien RAM vorhaelt. Da Lucene-Indexsegmente ueber Memory-Mapped-Files gelesen werden, profitieren wiederholte Zugriffe auf dieselben Segmente direkt vom Betriebssystem-Cache, ganz ohne explizite Konfiguration in Elasticsearch selbst.

Fuer JVM-Heap-Tuning folgt daraus eine klare Konsequenz: der RAM, der nicht dem Heap zugewiesen wird, ist kein verschwendeter Speicher, sondern aktiv genutzter Cache fuer den haeufigsten Zugriffspfad in einem Suchsystem, naemlich das Lesen von Indexdaten. Ein Data-Node mit sechzehn Gigabyte RAM und acht Gigabyte Heap hat effektiv acht Gigabyte fuer den Dateicache zur Verfuegung, ein Node mit vierundsechzig Gigabyte RAM und zweiunddreissig Gigabyte Heap hat entsprechend deutlich mehr Cache-Kapazitaet fuer denselben relativen Heap-Anteil.

5. Heap konkret konfigurieren

In modernen Elasticsearch-Versionen wird der Heap ueber Dateien im Verzeichnis config/jvm.options.d/ konfiguriert, statt die zentrale jvm.options-Datei direkt zu editieren. Das erleichtert automatisiertes Deployment, da eigene Heap-Einstellungen unabhaengig von Elasticsearch-Updates erhalten bleiben. Xms und Xmx, die minimale und maximale Heap-Groesse, sollten beim JVM-Heap-Tuning immer identisch gesetzt werden, um zu verhindern, dass die JVM den Heap zur Laufzeit dynamisch vergroessert, was selbst zu Pausen fuehrt.

Alternativ kann seit neueren Versionen die Umgebungsvariable ES_JAVA_OPTS oder die automatische Heap-Erkennung von Elasticsearch genutzt werden, die standardmaessig fuenfzig Prozent des verfuegbaren RAM bis zur Compressed-Oops-Grenze waehlt. Diese automatische Erkennung ist ein guter Startpunkt, ersetzt aber keine bewusste Ueberpruefung fuer produktive Data-Nodes mit spezifischen Workload-Anforderungen.


# Alternative: override heap via environment variable at container start
# Useful for Docker or Kubernetes deployments with per-node RAM sizes
ES_JAVA_OPTS="-Xms31g -Xmx31g"

# Elasticsearch also ships an automatic heap sizing script that picks
# roughly 50 percent of available RAM up to the compressed oops limit,
# a reasonable default but always worth verifying for production nodes
RAM des Nodes Empfohlener Heap Dateicache verbleibend Compressed Oops
16 GB 8 GB 8 GB aktiv
32 GB 16 GB 16 GB aktiv
64 GB 31 GB 33 GB aktiv
128 GB 31 GB (nicht 64 GB) 97 GB aktiv
128 GB (falsch) 64 GB 64 GB deaktiviert

6. Garbage Collector: G1GC richtig einstellen

Seit Elasticsearch 7 ist G1GC der Standard-Garbage-Collector und fuer die allermeisten Workloads die richtige Wahl, da er im Vergleich zum aelteren CMS-Collector kuerzere und vorhersehbarere Pausen erzeugt. G1GC arbeitet regionenbasiert und kann konfiguriert werden, welche maximale Pausenzeit angestrebt wird, ueber -XX:MaxGCPauseMillis, wobei diese Einstellung als Zielwert und nicht als harte Garantie zu verstehen ist.

Fuer die meisten Produktionscluster ist es beim JVM-Heap-Tuning sinnvoller, die Standardwerte von G1GC beizubehalten und stattdessen die Heap-Groesse selbst zu optimieren, statt einzelne GC-Parameter manuell zu justieren. Detailliertes GC-Tuning jenseits der Heap-Groesse ist meist nur bei sehr spezifischen Workload-Mustern noetig, etwa extremen Bulk-Indexierungsraten, und sollte auf Basis von GC-Logs entschieden werden, nicht auf Verdacht.


# jvm.options.d/gc.options
# G1GC is the default since Elasticsearch 7, usually leave defaults untouched
-XX:+UseG1GC
-XX:G1ReservePercent=25
-XX:InitiatingHeapOccupancyPercent=30

# Enable GC logging to diagnose pause issues before tuning further
-Xlog:gc*,gc+age=trace,safepoint:file=/var/log/elasticsearch/gc.log:utctime,pid,tags:filecount=32,filesize=64m

7. GC-Pausen erkennen und vermeiden

Lange GC-Pausen sind fuer Elasticsearch besonders gefaehrlich, weil ein Node, der laenger als der konfigurierte Timeout in einer Stop-the-World-Pause haengt, vom Cluster als nicht erreichbar eingestuft werden kann. Auf einem Data-Node fuehrt das zu Shard-Neuzuteilungen, auf einem Master-Node im schlimmsten Fall zu einem ungewollten Master-Wechsel. Die Log-Zeile [gc][young] mit ungewoehnlich hoher Dauer in den Elasticsearch-Logs ist meist das erste sichtbare Symptom.

Die haeufigste Ursache fuer lange GC-Pausen ist ein zu voller Heap durch Anfragen, die grosse Datenmengen im Speicher aufbauen, etwa Aggregationen mit hoher Kardinalitaet oder fielddata auf Textfeldern. Effektives JVM-Heap-Tuning bedeutet deshalb nicht nur, die richtige Heap-Groesse zu waehlen, sondern auch, problematische Query-Muster zu identifizieren, die den Heap unnoetig unter Druck setzen, bevor eine reine Groessenanpassung das eigentliche Problem verdeckt.

8. Circuit Breaker als Schutz vor Heap-Ueberlauf

Elasticsearch verwendet mehrere Circuit Breaker, die Operationen aktiv abbrechen, bevor sie den Heap zum Ueberlaufen bringen und einen OutOfMemoryError ausloesen. Der indices.breaker.total.limit begrenzt den insgesamt fuer Anfragen reservierbaren Heap-Anteil, waehrend spezifischere Breaker wie fielddata oder request gezielt einzelne Speicherbereiche schuetzen. Ein ausgeloester Circuit Breaker ist unangenehm fuer die betroffene Anfrage, aber deutlich besser als ein abgestuerzter Node.

Bei haeufig ausloesenden Circuit Breakern ist das kein Grund, sie einfach hoeher zu setzen, sondern ein Signal fuer echtes JVM-Heap-Tuning: entweder ist der Heap zu klein fuer die tatsaechliche Workload, oder bestimmte Anfragen sind ineffizient und sollten optimiert werden, etwa durch Nutzung von doc_values statt fielddata fuer Aggregationen und Sortierungen auf Textfeldern.


GET _nodes/stats/breaker

// Response excerpt for one data node, tripped_count above zero
// means a breaker actually rejected a request due to heap pressure
{
  "nodes": {
    "abc123": {
      "breakers": {
        "request": { "limit_size_in_bytes": 6442450944, "estimated_size_in_bytes": 1288490188, "tripped": 0 },
        "fielddata": { "limit_size_in_bytes": 4295098368, "estimated_size_in_bytes": 4290000000, "tripped": 12 },
        "parent": { "limit_size_in_bytes": 16106127360, "estimated_size_in_bytes": 8100000000, "tripped": 0 }
      }
    }
  }
}

Mironsoft

Elasticsearch- und OpenSearch-Betrieb, Heap-Tuning und Performance-Analyse

Lange GC-Pausen und Heap-Fehler im Griff behalten?

Wir analysieren GC-Logs, identifizieren speicherhungrige Query-Muster und finden die passende Heap-Konfiguration fuer eure Data-Nodes, ohne pauschale Werte zu uebernehmen.

GC-Log-Analyse

Pausenzeiten auswerten und Heap-Groesse gegen tatsaechliche Auslastung pruefen

Query-Optimierung

Speicherhungrige Aggregationen und fielddata-Nutzung identifizieren und ersetzen

Sizing-Beratung

Heap- und RAM-Dimensionierung fuer neue oder bestehende Data-Nodes festlegen

9. Heap-Nutzung dauerhaft im Blick behalten

Einmaliges JVM-Heap-Tuning beim Cluster-Setup reicht nicht aus, weil sich Workloads und Datenvolumen im Laufe der Zeit veraendern. Die _nodes/stats/jvm API liefert detaillierte Heap-Metriken pro Node, inklusive der Aufteilung nach Speicherbereichen wie Young- und Old-Generation. Ein dauerhaftes Monitoring dieser Werte zeigt fruehzeitig, ob der Heap konstant nahe der Obergrenze laeuft, was ein klares Signal fuer Nachbesserung ist.

Als Faustregel gilt: eine Heap-Auslastung, die dauerhaft ueber fuenfundsiebzig Prozent liegt und nach jedem Garbage-Collection-Zyklus nicht deutlich absinkt, deutet auf zu wenig Heap fuer die tatsaechliche Last hin. Umgekehrt zeigt eine dauerhaft niedrige Auslastung unter dreissig Prozent, dass Speicher fuer den Dateicache abgezweigt werden koennte. Gutes JVM-Heap-Tuning ist ein iterativer Prozess, der sich an echten Metriken orientiert, nicht an einer einmal gesetzten Konfiguration.


GET _cat/nodes?v&h=name,heap.percent,heap.current,heap.max,ram.percent

// heap.percent sustained above 75 percent across GC cycles
// is the clearest signal that heap sizing needs another look
name           heap.percent heap.current heap.max ram.percent
data-node-01   78           24.2gb       31gb     91
data-node-02   41           12.7gb       31gb     88
data-node-03   82           25.4gb       31gb     93

10. Zusammenfassung

Solides JVM-Heap-Tuning folgt zwei klaren Regeln: maximal fuenfzig Prozent des verfuegbaren RAM als Heap, und nie ueber die etwa zweiunddreissig Gigabyte Compressed-Oops-Grenze hinaus, weil darueber der effektive Speichergewinn durch groessere Pointer aufgezehrt wird. Der verbleibende RAM ist kein verschwendeter Speicher, sondern aktiv genutzter Lucene-Dateicache, der Suchanfragen direkt beschleunigt. G1GC als Standard-Collector sollte in den meisten Faellen nicht manuell nachjustiert werden.

Circuit Breaker schuetzen vor Heap-Ueberlauf, sind aber ein Symptom, kein Ziel: haeufig ausloesende Breaker zeigen entweder zu knappen Heap oder ineffiziente Anfragen. Wer GC-Logs und Heap-Metriken kontinuierlich beobachtet statt sich auf einmalig gesetzte Werte zu verlassen, erkennt Fehlkonfigurationen, bevor sie zu Node-Ausfaellen oder ungewollten Master-Wechseln fuehren.

JVM-Heap-Tuning: das Wichtigste auf einen Blick

50-Prozent-Regel

Nie mehr als die Haelfte des verfuegbaren RAM als Heap zuweisen, der Rest gehoert dem Dateicache.

32-GB-Grenze

Compressed Oops fallen oberhalb von rund zweiunddreissig Gigabyte weg, ein Heap knapp darunter ist meist effizienter.

Xms gleich Xmx

Minimale und maximale Heap-Groesse immer identisch setzen, verhindert Resizing-Pausen zur Laufzeit.

GC-Logs beobachten

Heap-Metriken und Pausenzeiten laufend pruefen, statt nach einmaliger Konfiguration nichts mehr nachzujustieren.

11. FAQ: JVM-Heap-Tuning fuer Elasticsearch

1Wie gross sollte der Elasticsearch-Heap maximal sein?
Maximal fuenfzig Prozent des RAM und nie ueber rund zweiunddreissig Gigabyte.
2Was sind Compressed Oops?
Komprimierte 32-Bit-Objektreferenzen unterhalb der Heap-Schwelle, sparen Speicher und verbessern Cache-Effizienz.
3Warum ist ungenutzter RAM neben dem Heap nicht verschwendet?
Er dient als Dateicache fuer Lucene-Segmente und beschleunigt Suchanfragen direkt.
4Warum muessen Xms und Xmx identisch sein?
Verhindert dynamisches Heap-Resizing zur Laufzeit, was selbst Pausen verursacht.
5Welcher Garbage Collector ist Standard?
G1GC seit Elasticsearch 7, Standardwerte reichen fuer die meisten Workloads.
6Was passiert bei einer langen GC-Pause?
Node kann als nicht erreichbar gelten, fuehrt zu Shard-Neuzuteilung oder Master-Wechsel.
7Was ist ein Circuit Breaker?
Bricht Operationen ab, bevor der Heap ueberlaeuft. Haeufiges Ausloesen signalisiert zu knappen Heap.
8Welche Heap-Auslastung gilt als kritisch?
Dauerhaft ueber fuenfundsiebzig Prozent ohne deutlichen Abfall nach GC-Zyklen.
9Sollte ich fielddata fuer Aggregationen aktivieren?
In der Regel nicht, doc_values sind der off-heap Standard fuer Sortierung und Aggregation.
10Wie ueberwache ich Heap-Nutzung laufend?
Ueber die _nodes/stats/jvm API und aktivierte GC-Logs, regelmaessig automatisiert ausgewertet.