Guida a Java String Pool

1. Panoramica

L' oggetto String è la classe più utilizzata nel linguaggio Java.

In questo rapido articolo, esploreremo il Java String Pool, la regione di memoria speciale in cui le stringhe vengono memorizzate dalla JVM .

2. String interning

Grazie all'immutabilità delle stringhe in Java, la JVM può ottimizzare la quantità di memoria allocata per loro memorizzando solo una copia di ogni stringa letterale nel pool . Questo processo è chiamato interning .

Quando creiamo una variabile String e le assegniamo un valore, la JVM cerca nel pool una String di uguale valore.

Se trovato, il compilatore Java restituirà semplicemente un riferimento al suo indirizzo di memoria, senza allocare memoria aggiuntiva.

Se non viene trovato, verrà aggiunto al pool (internato) e verrà restituito il suo riferimento.

Scriviamo un piccolo test per verificarlo:

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

3. Stringhe allocate utilizzando il costruttore

Quando creiamo una stringa tramite il nuovo operatore, il compilatore Java creerà un nuovo oggetto e lo memorizzerà nello spazio heap riservato alla JVM.

Ogni stringa creata in questo modo punterà a una regione di memoria diversa con il proprio indirizzo.

Vediamo come questo è diverso dal caso precedente:

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

4. String Literal vs String Object

Quando creiamo un oggetto String utilizzando l' operatore new () , crea sempre un nuovo oggetto nella memoria heap. D'altra parte, se creiamo un oggetto usando la sintassi letterale String, ad esempio "Baeldung", potrebbe restituire un oggetto esistente dal pool String, se esiste già. Altrimenti, creerà un nuovo oggetto String e lo inserirà nel pool di stringhe per un riutilizzo futuro.

Ad un livello alto, entrambi sono gli oggetti String , ma la differenza principale deriva dal punto in cui l' operatore new () crea sempre un nuovo oggetto String . Inoltre, quando creiamo una stringa utilizzando il valore letterale, viene internata.

Questo sarà molto più chiaro quando confronteremo due oggetti String creati usando String letterale e l' operatore new :

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

In questo esempio, gli oggetti String avranno lo stesso riferimento.

Successivamente, creiamo due oggetti diversi usando new e controlliamo che abbiano riferimenti diversi:

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

Allo stesso modo, quando confrontiamo un valore letterale String con un oggetto String creato utilizzando l' operatore new () utilizzando l'operatore ==, restituirà false:

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

In generale, dovremmo usare la notazione letterale String quando possibile . È più facile da leggere e offre al compilatore la possibilità di ottimizzare il nostro codice.

5. Tirocinio manuale

Possiamo internare manualmente una stringa nel pool di stringhe Java chiamando il metodo intern () sull'oggetto che vogliamo internare.

L'internamento manuale della stringa memorizzerà il suo riferimento nel pool e la JVM restituirà questo riferimento quando necessario.

Creiamo un test case per questo:

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

6. Raccolta dei rifiuti

Prima di Java 7, la JVM collocava il pool di stringhe Java nello spazio PermGen , che ha una dimensione fissa: non può essere espanso in fase di runtime e non è idoneo per la garbage collection .

Il rischio di internato stringhe nel PermGen (invece del Heap ) è che siamo in grado di ottenere un OutOfMemory errore dalla JVM se noi intern troppe stringhe .

Da Java 7 in poi, il pool di stringhe Java viene memorizzato nello spazio heap , che viene raccolto dalla JVM . Il vantaggio di questo approccio è il rischio ridotto di errore OutOfMemory perché le stringhe non referenziate verranno rimosse dal pool, rilasciando così la memoria.

7. Prestazioni e ottimizzazioni

In Java 6, l'unica ottimizzazione che possiamo eseguire è aumentare lo spazio PermGen durante l'invocazione del programma con l' opzione MaxPermSize JVM:

-XX:MaxPermSize=1G

In Java 7, abbiamo opzioni più dettagliate per esaminare ed espandere / ridurre la dimensione del pool. Vediamo le due opzioni per visualizzare la dimensione della piscina:

-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics

Se vogliamo aumentare la dimensione del pool in termini di bucket, possiamo utilizzare l' opzione StringTableSize JVM:

-XX:StringTableSize=4901

Prima di Java 7u40, la dimensione del pool predefinito era 1009 bucket, ma questo valore era soggetto ad alcune modifiche nelle versioni Java più recenti. Per essere precisi, la dimensione predefinita del pool da Java 7u40 fino a Java 11 era 60013 e ora è aumentata a 65536.

Notare che aumentare la dimensione del pool consumerà più memoria ma ha il vantaggio di ridurre il tempo necessario per inserire le stringhe nella tabella.

8. Una nota su Java 9

Fino a Java 8, le stringhe erano rappresentate internamente come un array di caratteri - char [] , codificato in UTF-16 , in modo che ogni carattere utilizzi due byte di memoria.

Con Java 9 viene fornita una nuova rappresentazione, chiamata Compact Strings. Questo nuovo formato sceglierà la codifica appropriata tra char [] e byte [] a seconda del contenuto memorizzato.

Poiché la nuova rappresentazione String utilizzerà la codifica UTF-16 solo quando necessario, la quantità di memoria heap sarà notevolmente inferiore, il che a sua volta causa un minore overhead di Garbage Collector sulla JVM.

9. Conclusione

In questa guida, abbiamo mostrato come la JVM e il compilatore Java ottimizzano le allocazioni di memoria per gli oggetti String tramite Java String Pool.

Tutti gli esempi di codice utilizzati nell'articolo sono disponibili su GitHub.