traduzione - java garbage collector force




L'utilizzo di final per le variabili in Java migliora la garbage collection? (10)

Oggi i miei colleghi e io abbiamo una discussione sull'uso della parola chiave final in Java per migliorare la garbage collection.

Ad esempio, se scrivi un metodo come:

public Double doCalc(final Double value)
{
   final Double maxWeight = 1000.0;
   final Double totalWeight = maxWeight * value;
   return totalWeight;  
}

Dichiarare le variabili nel metodo final aiuterebbe la garbage collection a ripulire la memoria dalle variabili inutilizzate nel metodo dopo l'uscita del metodo.

È vero?


Alcuni punti da chiarire:

  • Il riferimento Nulling non dovrebbe aiutare GC. Se lo facesse, indicherebbe che le tue variabili sono sovradimensionate. Un'eccezione è il caso del nepotismo degli oggetti.

  • Non ci sono ancora allocazioni su stack in Java.

  • Dichiarare una variabile finale significa che non puoi (in condizioni normali) assegnare un nuovo valore a quella variabile. Dal momento che la finale non dice nulla su scope, non dice nulla sul suo effetto su GC.


Bene, non so sull'uso del modificatore "finale" in questo caso, o del suo effetto sul GC.

Ma posso dirti questo: il tuo uso di valori in Box piuttosto che di primitive (ad es. Double invece di double) allocherà quegli oggetti sull'heap piuttosto che sullo stack, e produrrà inutili spazzatura che il GC dovrà ripulire.

Io uso solo primitive in scatola quando richiesto da un'API esistente o quando ho bisogno di primati nullable.


Ecco un esempio leggermente diverso, uno con i campi del tipo di riferimento finale piuttosto che le variabili locali del tipo valore finale:

public class MyClass {

   public final MyOtherObject obj;

}

Ogni volta che crei un'istanza di MyClass, creerai un riferimento in uscita a un'istanza MyOtherObject e il GC dovrà seguire quel collegamento per cercare oggetti live.

La JVM utilizza un algoritmo GC di mark-sweep, che deve esaminare tutti i riferimenti live nelle posizioni "root" del GC (come tutti gli oggetti nello stack di chiamate corrente). Ogni oggetto live è "contrassegnato" come "vivo" e qualsiasi oggetto a cui fa riferimento un oggetto live viene anche contrassegnato come "vivo".

Dopo il completamento della fase mark, il GC trascina l'heap, liberando memoria per tutti gli oggetti non marcati (e compattando la memoria per gli oggetti live rimanenti).

Inoltre, è importante riconoscere che la memoria heap di Java è suddivisa in una "giovane generazione" e una "vecchia generazione". Tutti gli oggetti sono inizialmente assegnati nella giovane generazione (a volte indicato come "l'asilo nido"). Poiché la maggior parte degli oggetti ha vita breve, il GC è più aggressivo nel liberare la spazzatura recente dalla giovane generazione. Se un oggetto sopravvive a un ciclo di raccolta della giovane generazione, viene spostato nella vecchia generazione (a volte indicata come "generazione di ruolo"), che viene elaborata meno frequentemente.

Quindi, in cima alla mia testa, ho intenzione di dire "no, la modifica" finale "non aiuta il GC a ridurre il carico di lavoro".

Secondo me, la migliore strategia per ottimizzare la gestione della memoria in Java è eliminare i riferimenti spuri il più rapidamente possibile. Puoi farlo assegnando "null" a un riferimento a un oggetto non appena hai finito di usarlo.

O, meglio ancora, minimizzare la dimensione di ogni ambito di dichiarazione. Ad esempio, se dichiari un oggetto all'inizio di un metodo a 1000 righe e se l'oggetto rimane attivo fino alla chiusura dell'ambito del metodo (l'ultima parentesi graffa chiusa), l'oggetto potrebbe rimanere in vita per molto più tempo che effettivamente necessario.

Se si utilizzano metodi piccoli, con solo una dozzina di righe di codice, gli oggetti dichiarati all'interno di tale metodo cadranno fuori dall'ambito più rapidamente e il GC sarà in grado di svolgere gran parte del proprio lavoro all'interno del più efficiente giovane generazione. Non vuoi che gli oggetti vengano spostati nella vecchia generazione a meno che non sia assolutamente necessario.


GC agisce su referenze irraggiungibili. Questo non ha nulla a che vedere con "finale", che è semplicemente un'affermazione di un incarico una tantum. È possibile che alcuni GC di VM possano utilizzare "finale"? Non vedo come o perché.


L'unica volta che preferisco dichiarare le variabili locali come finale è quando:

  • Devo renderli definitivi in ​​modo che possano essere condivisi con alcune classi anonime (ad esempio: creare thread daemon e lasciargli accedere ad alcuni valori dal metodo di inclusione)

  • Voglio renderli definitivi (ad esempio: un valore che non dovrebbe / non viene sovrascritto per errore)

Aiuta nella raccolta dei rifiuti veloce?
AFAIK un oggetto diventa candidato alla raccolta GC se ha zero forti riferimenti ad esso e in tal caso non c'è alcuna garanzia che verranno immediatamente raccolti. In generale, si dice che un riferimento forte muoia quando esce dallo scope o l'utente lo riassegna esplicitamente a riferimento null, quindi, dichiarandoli definitivi significa che il riferimento continuerà ad esistere fino a che il metodo esiste (a meno che il suo ambito sia esplicitamente ristretto a un blocco interno specifico {}) perché non è possibile riassegnare le variabili finali. Quindi penso che wrt collection "finale" possa introdurre un ritardo indesiderato.


La dichiarazione di una variabile locale final non avrà effetto sulla garbage collection, ma significa che non puoi modificare la variabile. Il tuo esempio sopra non dovrebbe essere compilato mentre stai modificando la variabile totalWeight che è stata contrassegnata come final . D'altra parte, la dichiarazione di una primitiva ( double invece di Double ) final consente a tale variabile di essere inserita nel codice chiamante, in modo che possa causare un miglioramento della memoria e delle prestazioni. Questo è usato quando hai un numero di public static final Strings in una classe.

In generale, il compilatore e il runtime ottimizzeranno dove possono. È meglio scrivere il codice in modo appropriato e non cercare di essere troppo complicato. Usa final quando non vuoi che la variabile sia modificata. Supponiamo che il compilatore esegua semplici ottimizzazioni e, se ti preoccupi delle prestazioni o dell'uso della memoria, usa un profiler per determinare il vero problema.


No, enfaticamente non è vero.

Ricorda che il final non significa costante, significa solo che non puoi cambiare il riferimento.

final MyObject o = new MyObject();
o.setValue("foo"); // Works just fine
o = new MyObject(); // Doesn't work.

Potrebbero esserci alcune piccole ottimizzazioni basate sulla consapevolezza che la JVM non dovrà mai modificare il riferimento (come non avere il controllo per vedere se è cambiato), ma sarebbe così lieve da non preoccuparsi.

Final dovrebbe essere pensata come utile meta-dati per lo sviluppatore e non come ottimizzazione del compilatore.


Sembra che ci siano molte risposte che sono congetture vaganti. La verità è che non esiste un modificatore finale per le variabili locali a livello di bytecode. La macchina virtuale non saprà mai che le tue variabili locali sono state definite come definitive o meno.

La risposta alla tua domanda è un enfatico no.


assolutamente, a patto che la vita dell'oggetto sia più breve e produca grandi benefici nella gestione della memoria, recentemente abbiamo esaminato la funzionalità di esportazione con variabili di istanza su un test e un altro test con variabile locale a livello di metodo. durante il test di carico, JVM getta outofmemoryerror al primo test e JVM viene fermato. ma in un secondo test, è riuscito a ottenere il report correttamente grazie a una migliore gestione della memoria.


final su variabili e parametri locali non fa differenza per i file di classe prodotti, quindi non può influenzare le prestazioni di runtime. Se una classe non ha sottoclassi, HotSpot tratta tale classe come se fosse definitiva in ogni caso (può annullare successivamente se una classe che interrompe quell'assunzione viene caricata). Credo che i metodi final siano più o meno gli stessi delle classi. final su campo statico può consentire alla variabile di essere interpretata come una "costante in fase di compilazione" e l'ottimizzazione deve essere eseguita da javac su tale base. final sui campi consente alla JVM una certa libertà di ignorare le relazioni di avvenimenti prima .





final