Anleitung zum Java String Pool

1. Übersicht

Das String- Objekt ist die am häufigsten verwendete Klasse in der Java-Sprache.

In diesem kurzen Artikel untersuchen wir den Java-String-Pool - den speziellen Speicherbereich, in dem Strings von der JVM gespeichert werden .

2. String Interning

Dank der Unveränderlichkeit von Strings in Java kann die JVM die Menge an Speicher für sie zugeordnet optimieren Speicherung nur eine Kopie jeder wörtlichen String im Pool . Dieser Vorgang wird als Internierung bezeichnet .

Wenn wir eine String- Variable erstellen und ihr einen Wert zuweisen, durchsucht die JVM den Pool nach einem String mit gleichem Wert.

Wenn gefunden, gibt der Java-Compiler einfach einen Verweis auf seine Speicheradresse zurück, ohne zusätzlichen Speicher zuzuweisen.

Wenn es nicht gefunden wird, wird es dem Pool hinzugefügt (interniert) und seine Referenz wird zurückgegeben.

Schreiben wir einen kleinen Test, um dies zu überprüfen:

String constantString1 = "Baeldung"; String constantString2 = "Baeldung"; assertThat(constantString1) .isSameAs(constantString2);

3. Mit dem Konstruktor zugewiesene Zeichenfolgen

Wenn wir einen String über den neuen Operator erstellen, erstellt der Java-Compiler ein neues Objekt und speichert es im für die JVM reservierten Heap-Bereich.

Jeder so erstellte String verweist auf einen anderen Speicherbereich mit einer eigenen Adresse.

Mal sehen, wie sich das vom vorherigen Fall unterscheidet:

String constantString = "Baeldung"; String newString = new String("Baeldung"); assertThat(constantString).isNotSameAs(newString);

4. String Literal vs String Object

Wenn wir ein String- Objekt mit dem Operator new () erstellen , wird immer ein neues Objekt im Heap-Speicher erstellt. Wenn wir andererseits ein Objekt mit der String- Literal-Syntax erstellen, z. B. „Baeldung“, kann es ein vorhandenes Objekt aus dem String-Pool zurückgeben, sofern es bereits vorhanden ist. Andernfalls wird ein neues String-Objekt erstellt und zur zukünftigen Wiederverwendung in den String-Pool gestellt.

Auf hoher Ebene sind beide die String- Objekte, aber der Hauptunterschied besteht darin, dass der Operator new () immer ein neues String- Objekt erstellt. Wenn wir einen String mit Literal erstellen , wird er auch interniert.

Dies wird viel deutlicher, wenn wir zwei mit dem String- Literal erstellte String- Objekte und den neuen Operator vergleichen:

String first = "Baeldung"; String second = "Baeldung"; System.out.println(first == second); // True

In diesem Beispiel haben die String- Objekte dieselbe Referenz.

Als Nächstes erstellen wir zwei verschiedene Objekte mit new und überprüfen, ob sie unterschiedliche Referenzen haben:

String third = new String("Baeldung"); String fourth = new String("Baeldung"); System.out.println(third == fourth); // False

Wenn wir ein String- Literal mit einem String- Objekt vergleichen, das mit dem Operator new () unter Verwendung des Operators == erstellt wurde, wird false zurückgegeben:

String fifth = "Baeldung"; String sixth = new String("Baeldung"); System.out.println(fifth == sixth); // False

Im Allgemeinen sollten wir nach Möglichkeit die String- Literal-Notation verwenden . Es ist einfacher zu lesen und gibt dem Compiler die Möglichkeit, unseren Code zu optimieren.

5. Manuelles Internieren

Wir können einen String im Java-String-Pool manuell internieren, indem wir die intern () -Methode für das Objekt aufrufen, das wir internieren möchten.

Durch manuelles Internieren des Strings wird seine Referenz im Pool gespeichert, und die JVM gibt diese Referenz bei Bedarf zurück.

Erstellen wir einen Testfall dafür:

String constantString = "interned Baeldung"; String newString = new String("interned Baeldung"); assertThat(constantString).isNotSameAs(newString); String internedString = newString.intern(); assertThat(constantString) .isSameAs(internedString);

6. Müllabfuhr

Vor Java 7 hat die JVM den Java- Zeichenfolgenpool im PermGen- Bereich abgelegt , der eine feste Größe hat. Er kann zur Laufzeit nicht erweitert werden und ist nicht für die Speicherbereinigung berechtigt .

Das Risiko der Internierung Strings im PermGen (anstelle des Heap ) ist , dass wir einen bekommen können OutOfMemory Fehler aus der JVM , wenn wir intern zu viele Strings .

Ab Java 7 wird der Java-String-Pool im Heap- Bereich gespeichert , der von der JVM gesammelter Müll ist . Der Vorteil dieses Ansatzes ist das verringerte Risiko eines OutOfMemory- Fehlers, da nicht referenzierte Zeichenfolgen aus dem Pool entfernt werden, wodurch Speicher freigegeben wird.

7. Leistung und Optimierungen

In Java 6 können wir nur den PermGen- Speicherplatz während des Programmaufrufs mit der Option MaxPermSize JVM erhöhen :

-XX:MaxPermSize=1G

In Java 7 haben wir detailliertere Optionen zum Untersuchen und Erweitern / Reduzieren der Poolgröße. Sehen wir uns die beiden Optionen zum Anzeigen der Poolgröße an:

-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics

Wenn wir die Poolgröße in Bezug auf Buckets erhöhen möchten, können wir die JTM- Option StringTableSize verwenden :

-XX:StringTableSize=4901

Vor Java 7u40 betrug die Standardpoolgröße 1009 Buckets, dieser Wert unterlag jedoch einigen Änderungen in neueren Java-Versionen. Um genau zu sein, war die Standardpoolgröße von Java 7u40 bis Java 11 60013 und stieg jetzt auf 65536.

Beachten Sie, dass das Erhöhen der Poolgröße mehr Speicher verbraucht, jedoch den Vorteil hat, dass weniger Zeit zum Einfügen der Zeichenfolgen in die Tabelle erforderlich ist .

8. Ein Hinweis zu Java 9

Bis Java 8 wurden Strings intern als Array von Zeichen dargestellt - char [] , das in UTF-16 codiert ist , sodass jedes Zeichen zwei Byte Speicher benötigt.

Mit Java 9 wird eine neue Darstellung namens Compact Strings bereitgestellt . Dieses neue Format wählt je nach gespeichertem Inhalt die entsprechende Codierung zwischen char [] und byte [] .

Da die neue String- Darstellung nur bei Bedarf die UTF-16- Codierung verwendet, ist der Heapspeicher erheblich geringer, was wiederum zu einem geringeren Garbage Collector- Overhead in der JVM führt.

9. Fazit

In diesem Handbuch haben wir gezeigt, wie die JVM und der Java-Compiler die Speicherzuordnungen für String- Objekte über den Java-String-Pool optimieren .

Alle im Artikel verwendeten Codebeispiele sind auf GitHub verfügbar.