2011-12-12 3 views
7

Recentemente ho letto bit e frammenti sulla garbage collection (principalmente in Java) e una domanda rimane ancora senza risposta: come fa una JVM (o il sistema di runtime in generale) a tenere traccia degli oggetti live CURRENTLY?Garbage collection - root node

Capisco che ci siano gli oggetti attualmente in pila, quindi tutte le variabili locali oi parametri di funzione, che sono oggetti. Il problema con questo approccio è che ogni volta che il sistema di runtime verifica quello che è attualmente in pila, in che modo distinguerebbe tra una variabile di riferimento e una semplice int? non può, vero?

Pertanto, ci deve essere una sorta di meccanismo per consentire runtime di costruire primo elenco di oggetti in tempo reale di passare per la fase mark-sweep ...

risposta

4

Ho trovato che la risposta fornita da Greyfairer è errata. Il runtime JVM non raccoglie il set di root dallo stack osservando quali bytecode vengono utilizzati per inviare i dati nello stack. Il frame dello stack è costituito da slot da 4 byte (arco a 32 bit). Ogni slot può essere un riferimento a un oggetto heap o un valore primitivo come un int. Quando è necessario un GC, il runtime esegue la scansione dello stack, dall'alto verso il basso. Per ogni slot, contiene un riferimento se:

a. È allineato al limite di 4 byte.

b. Il valore nello slot punta alla regione dell'heap (tra limite inferiore e limite superiore).

c. L'allocbit è impostato. L'allocbit è un indicatore che indica se la posizione di memoria corrispondente ad esso è allocata o meno.

Ecco il mio riferimento: http://www.ibm.com/developerworks/ibm/library/i-garbage2/.

Esistono altre tecniche per trovare il set di root (non in Java). Ad esempio, poiché i puntatori sono solitamente allineati al limite di 4/8 byte, il primo bit può essere utilizzato per indicare se uno slot è un valore o un puntatore primitivo: per i valori primitivi, il primo bit è impostato su 1. Lo svantaggio di questo è che hai solo 31 bit (arco a 32 bit) per rappresentare l'intero, e ogni operazione sui valori primitivi implica lo spostamento, che è ovvio un sovraccarico.

Inoltre, è possibile rendere tutti i tipi inclusi int allocati nell'heap. Cioè, tutte le cose sono oggetti. Quindi tutti gli slot in uno stack frame sono quindi riferimenti.

+0

Quindi tutto sommato è piuttosto una differenziazione di basso livello, piuttosto che JVM? Ma JVM ha un tipo di riferimento dichiarato per il bytecode, quindi perché non usarlo? Sei sicuro che sia così basso livello piuttosto che a livello di codice byte? – Bober02

+1

Per quanto ne so (basato sia sul collegamento che ho dato in precedenza, sia sulla navigazione dei codici di diverse implementazioni JVM), sono certo che la mia comprensione sia giusta. Puoi semplicemente immergerti nei codici GC di alcune implementazioni JVM open source per verificarlo. Hanno tutti bisogno di camminare per vedere il riferimento. Tuttavia, forse i criteri utilizzati per verificare se uno slot è di riferimento o meno è leggermente diverso (la maggior parte di essi verifica a e eb, per c, si basa davvero sull'implementazione). – Rainfield

+0

Perché non usare il bytecode, questa è la mia comprensione (non sono sicuro che sia giusto o no). GC è una cosa di runtime, ma i bytecode sono generati in fase di compilazione e statici. Quando si verifica un GC, il sistema di runtime deve trovare le radici e seguirle per scoprire gli oggetti in tempo reale. . Per fare ciò, devi effettivamente controllare il valore in ogni slot del frame stack, anche se sai che questo slot contiene un riferimento in fase di compilazione (come ha detto greyfairer, lo sai osservando il bytecode). Perché è necessario conoscere il valore di riferimento esatto per trovare altri oggetti nell'heap. – Rainfield

2

Il runtime è perfettamente in grado di distinguere tra le variabili di riferimento e primitivi, perché è nel bytecode compilato.

Ad esempio se una funzione f1 chiama una funzione f2 (int i, Object o, long l), la funzione di chiamata f1 spingerà 4 byte nello stack (o in un registro) che rappresenta i, 4 (o 8?) byte per il riferimento a o e 8 byte per l. La funzione chiamata f2 sa dove trovare questi byte nello stack e potrebbe potenzialmente copiare il riferimento a qualche oggetto sull'heap, oppure no. Quando ritorna la funzione f2, la funzione chiamante eliminerà i parametri dallo stack.

Il runtime interpreta il bytecode e tiene traccia di ciò che fa push o drop nello stack, in modo che sappia che cos'è un riferimento e che cos'è un valore primitivo.

Secondo http://www.javacoffeebreak.com/articles/thinkinginjava/abitaboutgarbagecollection.html, java utilizza un tracing garbage collector e non un algoritmo di conteggio dei riferimenti.

+0

Grazie per la risposta. Con questo in mente, come procede la raccolta dei rifiuti quando viene avviata da JVM? in che modo localizza i nodi root, saltando indietro nello stack o ha una collezione separata di nodi? – Bober02

+0

Vedere il collegamento dell'articolo per una dissezione approfondita. – greyfairer

+0

Ho trovato la seguente frase nell'articolo che hai indicato "Mark and sweep segue la stessa logica di iniziare dallo stack e dall'archiviazione statica e tracciare tutte le maniglie per trovare oggetti live." Quali sono queste maniglie mistiche a cui si riferiscono ... – Bober02

0

La macchina virtuale HotSpot genera una mappa GC per ciascuna subroutine compilata che contiene informazioni su dove si trovano le radici.Ad esempio, supponiamo che ha compilato una subroutine in codice macchina (il principio è lo stesso per il codice byte), che è lungo 120 byte, quindi la mappa GC perché potrebbe sembrare qualcosa di simile:

0 : [RAX, RBX] 
4 : [RAX, [RSP+0]] 
10 : [RBX, RSI, [RSP+0]] 
... 
120 : [[RSP+0],[RSP+8]] 

Qui [RSP+x] è supposto per indicare le posizioni dello stack e i registri R??. Quindi se il thread viene interrotto nell'istruzione di assemblaggio all'offset 10 e un ciclo gc viene eseguito, HotSpot sa che le tre radici sono in RBX, RSI e [RSP+0]. Traccia quelle radici e aggiorna i puntatori se deve spostare gli oggetti.

Il formato che ho descritto per la mappa di GC è solo per dimostrare il principio e, ovviamente, non quello che effettivamente usa HotSpot. Non è completo perché non contiene informazioni sui registri e sugli stack stack che contengono valori live primitivi e non è efficiente in termini di spazio per utilizzare un elenco per ogni offset di istruzione. Esistono molti modi in cui è possibile inserire le informazioni in un modo molto più efficiente.