2012-02-27 7 views
19

Sto leggendo sulla messa a punto JVM, e mi venne in mente che la JVM mantiene gli oggetti in giro quando lo fa GC in movimento. Ma gli oggetti Java hanno riferimenti l'uno all'altro, che si presume siano implementati come puntatori, ma la JVM non può mai scavalcare l'intero heap ogni volta che sposta oggetti e aggiorna tutti i riferimenti; sicuramente ciò richiederebbe per sempre. Quindi, come risolve i riferimenti, se i riferimenti non cambiano, ma la posizione fisica degli oggetti?Se la JVM continua a spostare gli oggetti quando fa GC, come risolve i riferimenti?

ho letto molto sulla JVM, ma che non è mai stato spiegato, o addirittura accennato a, ovunque.

[EDIT] Il mio punto è che i riferimenti sono cose a senso unico. Passare dal puntatore a quello puntato è "istantaneo", ma andare nel contrario richiederebbe una scansione completa dell'heap. Mentre è possibile, sembra improbabile. Se gli oggetti 10K sopravvivono a una raccolta secondaria, quanto tempo impiegherebbe per eseguire una scansione completa dell'heap per 10 volte per aggiornare i riferimenti a tali oggetti? Ci deve essere un qualche tipo di algoritmo o struttura ottimizzati usati.

+4

Domanda simile: http://stackoverflow.com/questions/88852/does-the-java-vm-move-objects-in-memory-and-if-so-how – sleske

risposta

13

Se sei veramente interessato a come funzionano i garbage collector, posso consigliare i 2 libri di Richard Jones su Garbage Collection. Link/riferimenti sono here. Questo non riguarda specificamente la garbage collection di Java.

(ho una copia del libro vecchio e il nuovo si è sulla mia lista della spesa.)


Ecco una versione semplice di come una copia offerte collettore con questo problema.

Una copia collettore funziona copiando oggetti da uno spazio (da-spazio) ad un altro (il a-spazio).

In particolare, il GC percorre il grafico di oggetti raggiungibili all'interno del "da" spazio, a partire da ciascuna delle radici GC. Ogni volta che trova un riferimento a un nodo (in un campo istanza, in un campo statico, in uno stack frame, ecc.), Controlla l'oggetto a cui punta il riferimento per vedere se è stato contrassegnato come visitato.

  • Se non è ancora segnato, il GC esegue le seguenti operazioni:

    1. segna l'oggetto nel da-spazio.
    2. Copia l'oggetto nello spazio.
    3. Memorizza l'indirizzo dell'oggetto nello spazio nell'oggetto from-space. (Questo è come un indirizzo di inoltro.)
    4. Visita in modo ricorsivo ogni campo di riferimento della copia spazio-spazio dell'oggetto.

    Il risultato di questo è il riferimento all'oggetto allo spazio.

  • Se l'oggetto è stato già segnato, il GC cerca l'indirizzo di spedizione, e restituisce quello.

La posizione (a-spazio o una radice GC) quando il GC ottenuto il riferimento del viene quindi aggiornato con il puntatore all'oggetto a-spazio.

Se si segue tutto questo, allora si vedrà che il GC non ha bisogno di andare alla ricerca di tutti i luoghi che contengono un riferimento a un dato oggetto spostato. Invece, incontra semplicemente tutti i luoghi nella traversata degli oggetti raggiungibili. Naturalmente, GC fa fare questa attraversamento, ma ci sono varie tecniche per ridurre la quantità di movimento che deve essere fatto in ogni ciclo del GC.

Se non avete seguito quanto sopra, quindi si prega di andare leggere uno dei libri di testo che ho consigliato. Faranno molto meglio a spiegarlo di quanto io possa fare. Troverai anche materiale su come altri tipi di CG trattano questo problema.


Java HotSpot Certificati Verdi sono tutti collezionisti di duplicazione di una forma o nell'altra.Le cose diventano un po 'più complicate della mia descrizione sopra per la raccolta parallela e simultanea, ma il meccanismo di "indirizzo di inoltro" è comune a tutti loro.

(non ci sono molti lavori pubblicati o altra documentazione pubblica su HotSpot Certificati Verdi, e la maggior parte del materiale che esiste presuppone che il lettore abbia una buona comprensione di come moderna lavoro netturbini.)

+0

Ma cosa succede se nella vecchia generazione ci sono oggetti che fanno riferimento a oggetti della nuova generazione (che vengono spostati)? Devi passare attraverso tutta la vecchia generazione alla ricerca di riferimenti a tutto ciò che viene spostato. Sembra che sarebbe più efficiente fare in modo che ogni riferimento passi attraverso uno strato indiretto che mantiene la posizione effettiva dell'oggetto. –

+0

Questo è specifico per GC. Ma l'approccio generale è che la JVM esegue una sequenza di "scrittura barriera" quando aggiorna un campo puntatore in un oggetto. La barriera di scrittura è responsabile di prendere nota del puntatore vecchio-> giovane generazione. (Ad esempio il raccoglitore G1 lo fa usando "carte" e "serie ricordate".) –

4

la JVM non può assolutamente andare oltre il mucchio intero dopo ogni volta che si muoveva oggetti intorno, e aggiornare tutti i riferimenti

Non sono un esperto su GC me stesso, ma per quanto riguarda Lo so, è più o meno quello che fa. Vedi per es. questo testo:

Al contrario, una copia collettore copia oggetti raggiungibili in un'altra regione di memoria mentre vengono attraversati. [...] Dopo tale un attraversamento tutti gli oggetti che sopravvivono risiedono in un'area contigua della memoria , e tutti i puntatori sono stati aggiornati in modo da puntare alle nuove posizioni dell'oggetto. [...] Durante il processo, il GC costruisce un oggetto grafico per monitorare gli oggetti "live" in modo che può aggiornare i riferimenti a qualsiasi oggetto che si muove.

(http://wiki.osdev.org/Garbage_collection#Copy_collectors, enfasi mia).

Per quanto riguarda questo "prendere per sempre", l'idea principale dietro un garbage collector di copia (o spostamento) è che è necessario spostare solo una piccola quantità di oggetti, poiché la maggior parte delle istanze è già morta (cioè la maggior parte le istanze hanno vita molto breve). Quindi il numero di oggetti che si muovono è piccolo, e si spera che il numero di riferimenti che puntano a loro sia anche abbastanza piccolo.

In ogni caso, il GC deve comunque compilare un elenco di riferimenti a oggetti (per scoprire quali oggetti sono ancora referenziati/vivi e devono essere copiati), quindi è probabile che riutilizzi tale elenco per aggiornare i riferimenti. Quindi l'unico aggiornamento è "lavoro extra".

+0

+1 per il riferimento, ma sfortunatamente non è specifico di JVM. Commenterò come una domanda di modifica ... –

+0

In realtà, il GC non va in tutto l'heap nel modo in cui l'OP descrive nella sua domanda ... –

1

la JVM non può superare l'intero heap dopo ogni istante in cui lo sposta gli oggetti e aggiorna tutti i riferimenti; sicuramente sarebbe prendere per sempre

lo è di sicuro la scansione attraverso l'intero mucchio di rilevare l'oggetto che non sono più referenziato da nessuno e contrassegnarli come ammissibili da raccogliere e di mettere tutti gli oggetti attivi in ​​una memoria compatta area per evitare la frammentazione.

Come lo fa dipende algoritmi di garbage collection utilizzati, ma è un processo che richiede tempo e che in effetti è un motivo per cui Java (per se) non può essere utilizzato in vincoli di tempo reale

+1

"scan through the whole heap" accade solo su * full GC *, ma la posizione degli oggetti cambia anche sul GC secondario, e quegli oggetti potrebbero essere puntati da oggetti nella vecchia generazione, che non fanno parte del GC secondario. –

+0

Ci sono molti algoritmi di garbage collection e anche jdk non usa lo stesso in 1.4 con 1.5 o successivi. Forse dovresti studiare l'algoritmo usato nella versione che ti interessa per ottenere la risposta esatta che cerchi – Cratylus

+0

Ho appena finito di leggere " Java Performance "(ISBN-10: 0137142528) pubblicato nell'ottobre 2011, che è IL riferimento. Sfortunatamente, questo non è spiegato (o in qualche modo l'ho perso). –

0

Di solito, i collezionisti dont cammina l'intero mucchio. Identificano oggetti vivi e li attraversano.

Ad esempio, il raccoglitore di copie in Hotspot inizia con le radici e identifica tutti gli oggetti in tempo reale. Una volta identificati gli oggetti live, questi vengono copiati in un nuovo spazio sull'heap. Passeggiando tutti gli oggetti live, esegue le modifiche di indirizzo richieste per gli oggetti live.

Una volta eseguita questa operazione, tutto ciò che rimane nel vecchio spazio sono gli oggetti morti e gli oggetti già spostati. Questo spazio libero viene recuperato da GC e viene utilizzato in futuro per spostare altri oggetti attivi in ​​esso.

Il tempo impiegato è proporzionale al numero di oggetti attivi nell'heap.

2

Non sono assolutamente sicuro che questo sia il modo in cui i riferimenti oggetto nell'heap sono gestiti, ma sospetto che l'oggetto di riferimento che Java VM distribuisce ai nostri programmi NON sono gli indirizzi di memoria effettivi ma i riferimenti interni di JVM quel punto all'indirizzo effettivo in JVM (HashMap o struttura simile). Cioè tutti gli oggetti che si riferiscono all'oggettoA avranno riferimenti [NOT address] all'oggettoA, quando GC si verifica JVM NON ha bisogno di aggiornare i riferimenti in tutti questi oggetti, solo l'effettivo indirizzo modificato nella propria HashMap.

+0

Questo è quello che stavo assumendo, ma l'altra risposta non sembra essere d'accordo. Sfortunatamente, finora non ci sono che congetture, dato che nessuno potrebbe indicare un collegamento Sun/Oracle che lo spieghi. –

Problemi correlati