2013-04-22 18 views
6

Ho letto molti articoli in conflitto relativi all'allocazione della memoria quando viene creato String. Alcuni articoli dicono che il nuovo operatore crea una stringa nell'heap e String letterale viene creato nel pool di stringhe [Heap] mentre alcuni dicono che il nuovo operatore crea un oggetto nell'heap e un altro oggetto nel pool di stringhe.Creazione di stringhe e char array Allocazione di memoria

Per analizzare questo ho scritto il seguente programma che stampa il hashcode di stringa char e String oggetto:

import java.lang.reflect.Field; 

public class StringAnalysis { 

    private int showInternalCharArrayHashCode(String s) 
      throws SecurityException, NoSuchFieldException, 
      IllegalArgumentException, IllegalAccessException { 
     final Field value = String.class.getDeclaredField("value"); 
     value.setAccessible(true); 
     return value.get(s).hashCode(); 
    } 

    public void printStringAnalysis(String s) throws SecurityException, 
      IllegalArgumentException, NoSuchFieldException, 
      IllegalAccessException { 
     System.out.println(showInternalCharArrayHashCode(s)); 

     System.out.println(System.identityHashCode(s)); 

    } 

    public static void main(String args[]) throws SecurityException, 
      IllegalArgumentException, NoSuchFieldException, 
      IllegalAccessException, InterruptedException { 
     StringAnalysis sa = new StringAnalysis(); 
     String s1 = new String("myTestString"); 
     String s2 = new String("myTestString"); 
     String s3 = s1.intern(); 
     String s4 = "myTestString"; 

     System.out.println("Analyse s1"); 
     sa.printStringAnalysis(s1); 

     System.out.println("Analyse s2"); 
     sa.printStringAnalysis(s2); 

     System.out.println("Analyse s3"); 
     sa.printStringAnalysis(s3); 

     System.out.println("Analyse s4"); 
     sa.printStringAnalysis(s4); 

    } 

} 

Questo programma stampa output seguente:

Analyse s1 
1569228633 
778966024 
Analyse s2 
1569228633 
1021653256 
Analyse s3 
1569228633 
1794515827 
Analyse s4 
1569228633 
1794515827 

Da questo un'uscita è chiaro che indipendentemente da come viene creata la stringa, se le stringhe hanno lo stesso valore condividono lo stesso array di caratteri.

Ora la mia domanda è dove è memorizzato questo chararray, è memorizzato in heap o va a permgen? Voglio anche capire come differire tra gli indirizzi di memoria heap e gli indirizzi di memoria permgen.

Ho un grosso problema se è memorizzato in permgen in quanto mangerà il mio prezioso spazio permgen limitato. e se il char array non è memorizzato in permgen ma in heap, allora implica che anche i letterali String usano lo spazio heap [che è qualcosa che non ho mai letto].

+0

Il compilatore Java è semplicemente troppo intelligente. Prova '" ... ". ACharArray()' o simile. Ma poi il livello di informazione scende a zero. –

+0

[questo] (http://www.precisejava.com/javaperf/j2se/StringAndStringBuffer.htm) potrebbe essere utile – Anirudha

+0

Sarebbe più convincente se si costruisse un 'String' da un' StringBuilder', magari chiamando un altro routine per aggiungere parti del valore della stringa. –

risposta

2

Da questa uscita una cosa è molto chiaro che indipendentemente da come viene creato String, se le stringhe hanno lo stesso valore, allora condividono stesso array char

Non del tutto: questo sta accadendo perché si inizia con una stringa letterale e crea più istanze da essa. Nell'implementazione OpenJDK (Sun/Oracle), l'array di supporto verrà copiato se rappresenta l'intera stringa. Si può vedere questo in src.jar, o qui: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java#String.%3Cinit%3E%28java.lang.String%29

Se si costruisce con attenzione le corde di origine in modo tale che partono da diversi array di caratteri, vi accorgerete che non condividono la matrice di supporto.

Ora la mia domanda è dove viene questa chararray memorizzato

Al meglio della mia conoscenza, l'array di caratteri per una stringa letterale viene memorizzato sul mucchio (quelli con una migliore conoscenza dei meccanismi interni classloading, si sentono libero di commentare). Le stringhe caricate dai file saranno sempre archiviare gli array di supporto nell'heap.

Quello che so per certo è che la struttura dati utilizzata da intern() fa riferimento solo all'oggetto String, non alla sua matrice di caratteri.

+0

Ho archiviato JLS, oggetti String se letterali o nuove stringhe sono effettivamente memorizzati nell'heap. Il pool di stringhe è solo un insieme di riferimenti. In questo modo l'array di char va davvero in pila. – Lokesh

1

Ultimo aggiornamento: per definizione, il valore letterale "myTestString" è internato e tutti i riferimenti String interni con lo stesso valore si riferiscono allo stesso oggetto String fisico. Quindi il letterale sarà EXACT SAME STRING come risultato da intern.

[Corretto] Per definizione, l'hashCode (ma non l'identityHashCode) di due stringhe con valori di sequenza di caratteri identici sarà identico.

L'hashCode di un array char[], d'altra parte, è semplicemente un guazzabuglio dei suoi bit di indirizzo e non ha alcuna relazione con il contenuto dell'array. Ciò indica che l'array value è, in tutti i casi precedenti, lo stesso array.

(Ulteriori informazioni: La vecchia implementazione di String includeva un puntatore a un valore char[], un offset, una lunghezza e un valore hash. (/ non Oracle non Sun) implementazioni altro eliminano la char[] matrice separata e comprendono i byte stringa all'interno all'assegnazione mucchio principale. non v'è alcun requisito che il campo value effettivamente esiste.)

[continua] copiato il caso di test e aggiunto alcune righe. hashCode e identityHashCode producono gli stessi valori su un dato char[], ma producono valori diversi su array diversi con lo stesso contenuto.

Il fatto che gli array siano identici in s1 e s2 è quasi certamente perché condividono l'array char[] del letterale internato "myTestString". Se le stringhe venissero costruite separatamente dagli array "freschi" char[], sarebbero diverse.

Il take-away principale di tutto questo è che i letterali String sono internati e l'implementazione in fase di test "prende in prestito" la matrice della sorgente quando una stringa viene copiata con new String(String).

Char array hash codes 
a1.hashCode() = 675303090 
a2.hashCode() = 367959235 
a1 identityHashCode = 675303090 
a2 identityHashCode = 367959235 
Strings from char arrays 
a1 String = ABCDE 
a1 String's hash = 62061635 
a1 String value's identityHashCode = 510044439 
a2 String = ABCDE 
a2 String's hash = 62061635 
a2 String value's identityHashCode = 1709651096 
+0

"Per definizione, String.hashCode e System.identityHashCode su una stringa restituiscono lo stesso valore" - hai un riferimento per questo? Perché certamente non è ciò che dicono [docs] (http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#identityHashCode (java.lang.Object)). – parsifal

+0

@parsifal - OK, mi hai portato lì - ho letto male le specifiche. identityHashCode restituisce presumibilmente la versione di "hash address address" dell'hash e quindi identifica diversi (ma "identici") oggetti. –

+0

@HotLicks: se vedi l'output il codice hash degli array di caratteri è lo stesso per tutte le stringhe. Quindi non è corretto "Quindi non sorprende che i primi due array siano diversi" – Lokesh

3

Da String src

public String(String original) { 
     this.value = original.value; 
     this.hash = original.hash; 
    } 

E 'chiaro che la stringa creato con questo costruttore azioni char (valore) con la stringa originale.

E 'importante notare che l'API non garantisce questa condivisione:

Inizializza un oggetto String appena creato in modo che rappresenta la stessa sequenza di caratteri come argomento; in altre parole, la stringa appena creata è una copia della stringa argomento. A meno che non sia necessaria una copia esplicita dell'originale, l'uso di questo costruttore non è necessario poiché le stringhe sono immutabili

Ad esempio, String.substring utilizzato per condividere il char array con la stringa originale, ma nelle ultime versioni di Java 1.7 String.substring crea una copia dell'array di caratteri.

+0

FYI, il tuo esempio sorgente proviene da una versione precedente di Java (1.5, suppongo). Ha portato a un * lotto * di eccezioni di memoria impreviste, motivo per cui le attuali versioni (1.6/1.7) guardano le dimensioni dell'array di supporto rispetto alla dimensione riportata della stringa. – parsifal

+0

@Evgeniy: questo spiega perché il char array è lo stesso per tutte le stringhe create usando new e lo condivide con String letteral. C'è un modo per testare dove questo array di char è stato creato in heap o permgen? – Lokesh

+1

se str == str.intern() significa che str era in permgen –