2010-05-26 16 views
16

Se si dispone di due istanze di una stringa e sono uguali, in Java condivideranno la stessa memoria. Come viene implementato sotto il cofano?In che modo java implementa il modello del peso mosca per la stringa sotto il cofano?

MODIFICA: La mia applicazione utilizza un numero elevato di oggetti String, molti dei quali identici. Qual è il modo migliore per utilizzare il pool costante di Java String, in modo da evitare la creazione dell'implementazione del peso mosca personalizzato?

risposta

6

Controllare il codice sorgente di java.lang.String (l'origine per l'intera java api fa parte del JDK).

Per riepilogare: A Stringa racchiude una sottosequenza di char[]. Il supporto char[] non viene mai modificato. Ciò è possibile non filtrando né catturando questo char[] all'esterno della classe String. Tuttavia, diversi Strings possono condividere lo stesso char[] (vedere Implementazione di String.substring).

C'è anche il meccanismo di internamento, come spiegato nelle altre risposte.

+0

Il fatto che 'String.substring' non allochi un nuovo' char [] 'ora non è più vero. Vedi [questa risposta] (http://stackoverflow.com/a/14161077/4464702). – RAnders00

+0

Questo è corretto.String non implementa più il modello del peso mosca, perché la condivisione dei riferimenti è ora considerata più costosa di quella del "peso" delle stringhe, in parte perché le JVM sono state migliorate per allocare oggetti sullo stack se l'analisi di escape dimostra che l'oggetto non può sopravvivere current stack frame - un'ottimizzazione che si basa sull'oggetto 'char []' non condiviso. – meriton

4

Non è necessario vero. Esempio:

String s1 = "hello"; 
String s2 = "hello"; 
System.out.println(s1 == s2); // true 

ma:

String s1 = new String("hello"); 
String s2 = new String("hello"); 
System.out.println(s1 == s2); // false 

Ora la seconda forma è scoraggiato. Alcuni (incluso me) pensano che String non dovrebbe nemmeno avere un costruttore pubblico. Una versione migliore di quanto sopra sarebbe:

String s1 = new String("hello").intern(); 
String s2 = new String("hello").intern(); 
System.out.println(s1 == s2); // true 

Ovviamente non c'è bisogno di fare questo per una costante String. È illustrativo.

Il punto importante di questo è che se stai passato un String o ottenere uno da una funzione non si può fare affidamento sul String essere canonica. Un canonicheObject soddisfa questa uguaglianza:

a.equals(b) == b.equals(a) == (a == b) 

per i non null casi a, b, di un dato Class.

+2

Una parola di avvertimento relativa all'internamento è che utilizza la memoria PermGen, che può risultare in un 'OutOfMemoryError' molto sgradevole. Se il pool di stringhe è necessario, un pool personalizzato è spesso una scelta migliore: http://hype-free.blogspot.com/2010/03/stringintern-there-are-better-ways.html – gustafc

+0

Da Java 7 in poi, stringhe internate non sono più nel PermGen. Se [questa risposta] (http://stackoverflow.com/a/16298053/4464702). @gustafc – RAnders00

6

I valori letterali delle stringhe sono internati in Java, quindi c'è davvero solo un oggetto String con più riferimenti (quando sono uguali, che non è sempre il caso). Vedere l'articolo java.net All about intern() per ulteriori dettagli.

C'è anche un buon esempio/spiegazione nella sezione 3.10.5 String Literals del JLS che parla di quando le stringhe sono internate e quando saranno distinte.

12

Se si dispone di due istanze di una stringa, e sono uguali, in Java che condivideranno la stessa memoria

Questo in realtà non è vero al 100%.

This blog post is a decent explanation del perché è così e che cos'è il pool costante di stringa.

+0

+1: questa risposta e la risposta di Bill the Lizard sono in realtà quelle che affrontano davvero la domanda. – haylem

3

Per rispondere alla domanda modificata, Sun JVM dispone di un'opzione -XX:+StringCache, che nella mia osservazione può ridurre in modo significativo l'ingombro di memoria di un'applicazione pesante in String.

Altrimenti, hai la possibilità di internare le tue stringhe, ma sarei attento a questo. Le stringhe molto grandi e non più referenziate continueranno a utilizzare la memoria per la vita della JVM.

Edit (in risposta al commento): ho scoperto che l'opzione StringCache da here:

-XX: + StringCache Abilita caching di stringhe comunemente assegnate.

Tom Hawtin descrive un tipo di memorizzazione nella cache per migliorare alcuni parametri di riferimento. La mia osservazione quando l'ho messa su IDEA è stata che l'impronta di memoria (dopo una raccolta completa dei rifiuti) è andata giù per non averla. Non è un parametro documentato, e potrebbe davvero riguardare l'ottimizzazione per alcuni benchmark. La mia osservazione è che ha aiutato, ma non vorrei costruire un sistema importante basato su di esso.

+0

Ho provato a trovare più informazioni su -XX: + StringCache ma senza risultati. Dove posso leggere di più su questa opzione e su come può ridurre il footprint della memoria? Avete ulteriori informazioni su cosa questa opzione fa per VM? – Dan

1

Due cose per essere attenti a:

  1. Non utilizzare new String("abc") costruttore, basta usare il letterale "abc".
  2. Impara ad usare il metodo intern() nella classe String. Soprattutto quando si concatenano stringhe insieme o quando si converte array di caratteri/array di byte/etc in una stringa.

intern() restituisce sempre le stringhe raggruppate.

0

Se le stringhe identiche provengono da un insieme fisso di valori possibili, quindi un'enumerazione sicura per tipo è ciò che si desidera qui. Non solo ridurrà il numero di stringhe, ma renderà l'applicazione più solida. Tutta la tua app saprà che questa stringa ha una sua semantica, forse anche alcuni metodi di convenienza.

Le mie ottimizzazioni preferite sono sempre quelle che possono essere difese rendendo il codice migliore, non solo più veloce. E 9 volte su 10, la sostituzione di una stringa con un tipo concreto porta a un codice più corretto e autodocumentante.