Dove vive il pool costante di String di Java, l’heap o lo stack?

Conosco il concetto di un pool di costanti e il pool di costanti stringa utilizzato da JVM per gestire i valori letterali stringa. Ma non so quale tipo di memoria viene utilizzata da JVM per memorizzare costanti letterali costanti. Lo stack o l’heap? Dal momento che è un letterale che non è associato ad alcuna istanza, suppongo che verrà archiviato in stack. Ma se non viene indicato da alcuna istanza, il valore letterale deve essere raccolto da GC run (correggimi se ho torto), quindi come viene gestito se è archiviato nello stack?

La risposta è tecnicamente né. In base alla specifica Java Virtual Machine, l’area per la memorizzazione di stringhe letterali si trova nel pool costante di runtime . L’area di memoria del pool di costanti di runtime viene allocata su una base per class o per interfaccia, quindi non è legata a nessuna istanza di object. Il pool di costanti di runtime è un sottoinsieme dell’area del metodo che “memorizza strutture per class come il pool costante di runtime, dati di campi e metodi e il codice per metodi e costruttori, inclusi i metodi speciali utilizzati in class e l’inizializzazione e l’interfaccia dell’istanza digitare inizializzazione “. Le specifiche della VM dicono che sebbene l’ area del metodo sia logicamente parte dell’heap, non impone che la memoria allocata nell’area del metodo sia soggetta alla garbage collection o ad altri comportamenti che verrebbero associati alle normali strutture dati allocate all’heap.

Come spiegato da questa risposta , la posizione esatta del lotto di stringhe non è specificata e può variare da un’implementazione JVM a un’altra.

È interessante notare che fino a Java 7, il pool era nello spazio permgen dell’heap su JVM hotspot ma è stato spostato nella parte principale dell’heap da Java 7 :

Area : HotSpot
Sinossi : In JDK 7, le stringhe internate non sono più allocate nella generazione permanente dell’heap Java, ma sono allocate nella parte principale dell’heap Java (noto come generazioni giovani e vecchie), insieme agli altri oggetti creati da l’applicazione. Questo cambiamento comporterà più dati residenti nell’heap principale di Java e meno dati nella generazione permanente e, pertanto, potrebbe richiedere la regolazione delle dimensioni dell’heap. La maggior parte delle applicazioni vedrà solo differenze relativamente piccole nell’utilizzo dell’heap a causa di questa modifica, ma applicazioni più grandi che caricano molte classi o fanno un uso massiccio del metodo String.intern () vedranno differenze più significative. RFE: 6962931

E in Java 8 Hotspot, la generazione permanente è stata completamente rimossa.

I valori letterali stringa non sono memorizzati nello stack.

I valori letterali delle stringhe (o, più esattamente, gli oggetti String che li rappresentano) sono stati storicamente memorizzati in un heap chiamato heap “permgen”. (Permgen è l’abbreviazione di generazione permanente.)

In circostanze normali, i valori letterali delle stringhe e la maggior parte degli altri elementi nell’heap di permgen sono “permanentemente” raggiungibili e non vengono raccolti. (Ad esempio, i valori letterali String sono sempre raggiungibili dagli oggetti codice che li utilizzano.) Tuttavia, è ansible configurare una JVM per tentare di trovare e raccogliere classi caricate dynamicmente che non sono più necessarie e questo potrebbe causare la raccolta di dati letterali di stringhe. .

CHIARIFICAZIONE # 1 – Non sto dicendo che Permgen non ottiene GC’ed. In genere, quando JVM decide di eseguire un GC completo. Il mio punto è che i letterali String saranno raggiungibili purché il codice che li usa sia raggiungibile, e il codice sarà raggiungibile fintanto che il classloader del codice è raggiungibile, e per i classloader predefiniti, che significa “per sempre”.

CLARIFICAZIONE # 2 – In effetti, Java 7 memorizza internamente oggetti String nel normale Heap. Ciò include (presumo) l’object String che rappresenta i letterali String. (Vedi la risposta di @ assylias per i dettagli.)

Pooling di stringhe

Il pooling delle stringhe (talvolta chiamato anche canonicalizzazione delle stringhe) è un processo che sostituisce diversi oggetti String con lo stesso valore ma un’id quadro diversa con un singolo object String condiviso. È ansible raggiungere questo objective mantenendo la propria mappa (con riferimenti eventualmente deboli o deboli in base alle proprie esigenze) e utilizzando i valori della mappa come valori canonizzati. Oppure puoi usare il metodo String.intern () che ti viene fornito da JDK.

In tempi di Java 6 l’utilizzo di String.intern () era vietato da molti standard a causa dell’elevata possibilità di ottenere un’OutMemoryException se il pooling andava fuori controllo. L’implementazione di Oracle Java 7 del pooling delle stringhe è stata notevolmente modificata. Puoi cercare i dettagli in http://bugs.sun.com/view_bug.do?bug_id=6962931 e http://bugs.sun.com/view_bug.do?bug_id=6962930 .

String.intern () in Java 6

In quei bei vecchi tempi tutte le stringhe internate erano memorizzate nel PermGen – la dimensione fissa dell’heap utilizzata principalmente per memorizzare classi caricate e pool di stringhe. Oltre alle stringhe internamente esplicite, il pool di stringhe PermGen conteneva anche tutte le stringhe letterali precedentemente utilizzate nel programma (la parola importante qui viene utilizzata – se una class o un metodo non è mai stato caricato / chiamato, le costanti definite in essa non verranno caricate).

Il problema più grande con questo pool di stringhe in Java 6 era la sua posizione: PermGen. PermGen ha una dimensione fissa e non può essere espanso in fase di runtime. Puoi impostarlo usando l’opzione -XX: MaxPermSize = 96m. Per quanto ne so, la dimensione PermGen predefinita varia tra 32 M e 96 M a seconda della piattaforma. Puoi aumentare le sue dimensioni, ma le sue dimensioni saranno comunque corrette. Tale limitazione richiedeva un uso molto attento di String.intern: è preferibile non internare alcun input utente non controllato utilizzando questo metodo. Ecco perché il pooling delle stringhe in tempi di Java 6 è stato implementato principalmente nelle mappe gestite manualmente.

String.intern () in Java 7

I tecnici Oracle hanno apportato una modifica estremamente importante alla logica del pool di stringhe in Java 7: il pool di stringhe è stato trasferito nell’heap. Significa che non sei più limitato da un’area di memoria a dimensione fissa separata. Tutte le stringhe ora si trovano nell’heap, come la maggior parte degli altri oggetti ordinari, che consente di gestire solo la dimensione dell’heap durante la regolazione dell’applicazione. Tecnicamente, questo da solo potrebbe essere un motivo sufficiente per riconsiderare l’uso di String.intern () nei programmi Java 7. Ma ci sono altri motivi.

I valori del pool di stringhe sono garbage collection

Sì, tutte le stringhe nel pool di stringhe JVM sono idonee per la garbage collection se non ci sono riferimenti alle radici del programma. Si applica a tutte le versioni discusse di Java. Significa che se la tua stringa internata è uscita dal campo di applicazione e non ci sono altri riferimenti ad essa, sarà raccolta dal gruppo di stringhe JVM.

Essere idoneo per la garbage collection e risiedere nell’heap, un pool di stringhe JVM sembra essere il posto giusto per tutte le stringhe, non è vero? In teoria è vero – le stringhe non usate saranno raccolte dal pool, le stringhe usate ti permetteranno di salvare la memoria nel caso in cui si ottenga una stringa uguale dall’input. Sembra essere una perfetta strategia di salvataggio della memoria? Quasi così. È necessario sapere come viene implementato il pool di stringhe prima di prendere qualsiasi decisione.

fonte.

Alle grandi risposte che ho già incluso qui voglio aggiungere qualcosa che manca nella mia prospettiva – e illustrazione.

Come già JVM divide la memoria allocata in un programma Java in due parti. uno è stack e un altro è heap . Lo stack viene utilizzato per scopi di esecuzione e l’heap viene utilizzato per scopi di archiviazione. In quella memoria heap, JVM assegna un po ‘di memoria specialmente per i letterali stringa. Questa parte della memoria heap è chiamata pool di costanti di stringhe .

Ad esempio, se si inizializzano i seguenti oggetti:

String s1 = "abc"; String s2 = "123"; String obj1 = new String("abc"); String obj2 = new String("def"); String obj3 = new String("456); 

I valori letterali delle stringhe s1 e s2 andranno al pool costante di stringhe, agli oggetti obj1, obj2, obj3 all’heap. Tutti loro, saranno referenziati dallo Stack.

Inoltre, si noti che “abc” apparirà in heap e in pool costante di stringhe. Perché String s1 = "abc" e String obj1 = new String("abc") verranno creati in questo modo? È perché String obj1 = new String("abc") crea esplicitamente una nuova istanza distinta referenzialmente di un object String s1 = "abc" e String s1 = "abc" può riutilizzare un’istanza dal pool di costanti di stringhe se ne è disponibile una. Per una spiegazione più elaborata: https://stackoverflow.com/a/3298542/2811258

inserisci la descrizione dell'immagine qui

Come altre risposte spiegano, la memoria in Java è divisa in due parti

1. Stack: viene creato uno stack per thread e memorizza i frame di stack che memorizzano nuovamente le variabili locali e se una variabile è un tipo di riferimento, quella variabile fa riferimento a una posizione di memoria nell’heap per l’object effettivo.

2. Heap: tutti i tipi di oggetti verranno creati solo in heap.

La memoria di heap è nuovamente suddivisa in 3 parti

1. Young Generation: memorizza oggetti che hanno una vita breve, la stessa Young Generation può essere divisa in due categorie Eden Space e Survivor Space .

2. Vecchia generazione: memorizza oggetti che sono sopravvissuti a molti cicli di garbage collection e che sono ancora referenziati.

3. Generazione permanente: memorizza i metadati relativi al programma, ad esempio il pool costante di runtime.

Il pool di costanti stringa appartiene all’area di generazione permanente della memoria Heap.

Come discusso in How Does JVM gestisce il sovraccarico e l’override dei metodi internamente , possiamo vedere il pool costante di runtime per il nostro codice nel bytecode usando javap -verbose class_name che mostrerà i riferimenti al metodo (#Methodref), gli oggetti Class (#Class), stringhe letterali (#String)

runtime-costante-piscina