Einführung in den JVM-Code-Cache

1. Einleitung

In diesem Tutorial werden wir uns kurz mit dem Code-Cache-Speicher der JVM befassen.

2. Was ist der Code-Cache?

Einfach ausgedrückt ist der JVM-Code-Cache ein Bereich, in dem JVM seinen in nativen Code kompilierten Bytecode speichert . Wir nennen jeden Block des ausführbaren nativen Codes eine nmethod . Die nm- Methode kann eine vollständige oder inline Java-Methode sein.

Der Just-in-Time-Compiler (JIT) ist der größte Verbraucher im Code-Cache-Bereich. Aus diesem Grund nennen einige Entwickler diesen Speicher einen JIT-Code-Cache.

3. Code-Cache-Optimierung

Der Code-Cache hat eine feste Größe . Sobald es voll ist, kompiliert die JVM keinen zusätzlichen Code, da der JIT-Compiler jetzt ausgeschaltet ist. Außerdem erhalten wir die Warnmeldung "CodeCache ist voll ... Der Compiler wurde deaktiviert ". Infolgedessen wird die Leistung unserer Anwendung beeinträchtigt. Um dies zu vermeiden, können wir den Code-Cache mit den folgenden Größenoptionen optimieren:

  • InitialCodeCacheSize - Die anfängliche Code-Cache-Größe beträgt standardmäßig 160 KB
  • ReservedCodeCacheSize - Die maximale Standardgröße beträgt 48 MB
  • CodeCacheExpansionSize - Die Erweiterungsgröße des Code-Cache, 32 KB oder 64 KB

Das Erhöhen von ReservedCodeCacheSize kann eine Lösung sein, dies ist jedoch normalerweise nur eine vorübergehende Problemumgehung.

Glücklicherweise bietet die JVM eine UseCodeCacheFlushing- Option, um das Leeren des Code-Cache-Bereichs zu steuern . Der Standardwert ist false. Wenn wir es aktivieren, wird der belegte Bereich freigegeben, wenn die folgenden Bedingungen erfüllt sind:

  • Der Code-Cache ist voll. Dieser Bereich wird gespült, wenn seine Größe einen bestimmten Schwellenwert überschreitet
  • Das bestimmte Intervall ist seit der letzten Bereinigung überschritten
  • Der vorkompilierte Code ist nicht heiß genug. Für jede kompilierte Methode verfolgt die JVM einen speziellen Hotness-Zähler. Wenn der Wert dieses Zählers kleiner als ein berechneter Schwellenwert ist, gibt die JVM diesen vorkompilierten Code frei

4. Verwendung des Code-Cache

Um die Verwendung des Code-Cache zu überwachen, müssen wir die Größe des aktuell verwendeten Speichers verfolgen.

Um Informationen zur Verwendung des Code-Cache zu erhalten, können Sie die Option –XX: + PrintCodeCache JVM angeben . Nach dem Ausführen unserer Anwendung sehen wir eine ähnliche Ausgabe:

CodeCache: size=32768Kb used=542Kb max_used=542Kb free=32226Kb 

Mal sehen, was jeder dieser Werte bedeutet:

  • Die Größe in der Ausgabe gibt die maximale Größe des Speichers an, die mit ReservedCodeCacheSize identisch ist
  • verwendet wird die tatsächliche Größe des aktuell verwendeten Speichers
  • max_used ist die maximale Größe, die verwendet wurde
  • frei ist der verbleibende Speicher, der noch nicht belegt ist

Die PrintCodeCache- Option ist sehr nützlich, da wir:

  • Sehen Sie, wann die Spülung erfolgt
  • Stellen Sie fest, ob wir einen kritischen Speicherauslastungspunkt erreicht haben

5. Segmentierter Code-Cache

Ab Java 9 unterteilt die JVM den Code-Cache in drei verschiedene Segmente, von denen jedes einen bestimmten Typ von kompiliertem Code enthält . Genauer gesagt gibt es drei Segmente:

  • Das Nicht-Methodensegment enthält internen JVM-Code wie den Bytecode-Interpreter. Standardmäßig beträgt dieses Segment ca. 5 MB. Außerdem ist es möglich , die Segmentgröße über das konfigurieren -XX: NonNMethodCodeHeapSize Tuning Flagge
  • Das Profilcodesegment enthält leicht optimierten Code mit möglicherweise kurzen Lebensdauern. Auch wenn die Segmentgröße rund 122 MB standardmäßig ist, können wir es über den ändern -XX: ProfiledCodeHeapSize Tuning Flagge
  • Das nicht profilierte Segment enthält vollständig optimierten Code mit möglicherweise langen Lebensdauern. Ebenso sind es standardmäßig etwa 122 MB. Dieser Wert ist natürlich, konfigurierbar über das -XX: NonProfiledCodeHeapSize Tuning Flagge

Diese neue Struktur behandelt verschiedene Arten von konformem Code unterschiedlich, was zu einer besseren Gesamtleistung führt.

Das Trennen von kurzlebigem kompiliertem Code von langlebigem Code verbessert beispielsweise die Leistung des Methoden-Sweepers - hauptsächlich, weil ein kleinerer Speicherbereich gescannt werden muss.

6. Fazit

Dieser kurze Artikel enthält eine kurze Einführung in den JVM-Code-Cache.

Darüber hinaus haben wir einige Verwendungs- und Optimierungsoptionen vorgestellt, um diesen Speicherbereich zu überwachen und zu diagnostizieren.