5

Quindi, ho scoperto di recente il metodo finalize in Java (non so perché l'ho perso prima, ma è così). Questo sembra come potrebbe essere la risposta a molti dei problemi con cui sto lavorando, ma volevo ottenere un po 'più di informazioni prima.Come finalize() funziona in java?

in linea, ho trovato questo schema che illustra il processo di raccolta dei rifiuti e finalizzare:

This describes the order of operations involving finalize and the JGC:

Un paio di domande:

  1. Questo avviene in un thread separato, giusto?
  2. Cosa succede se istanzio un nuovo oggetto durante la finalizzazione? È permesso?
  3. Cosa succede se richiamo un metodo statico da finalizzare?
  4. Cosa succede se stabilisco un nuovo riferimento all'oggetto da finalizzare?

Suppongo che dovrei spiegare perché mi interessa. Lavoro molto con LWJGL, e sembra che se potessi usare finalize per far sì che gli oggetti Java ripuliscano automaticamente le risorse OpenGL, allora potrei fare alcune cose davvero belle in termini di API.

+2

Questa non è una risposta diretta alla tua domanda, ma sappi che finalizzare non è garantito che venga mai chiamato, il che è uno dei tanti motivi per cui non viene usato pesantemente. (EDIT: e anche se lo fosse, verrà chiamato a causa della pressione della memoria nella memoria gestita da Java. Ciò significa che se stai cercando di usarlo per liberare altre risorse, allora potresti esaurirle e il tuo programma si arresta in modo anomalo anche se la pulitura potrebbe recuperarli tutti, solo perché Java non sa che l'esecuzione del GC riacquisirà (ad es.) i file handle). – jacobm

risposta

3

Non penso ci siano garanzie su quale thread verrà utilizzato. Nuovi oggetti possono essere istanziati e possono essere chiamati metodi statici. Stabilire un nuovo riferimento al tuo oggetto ti impedirà di raccogliere i dati inutili, ma il metodo finalize sarà non essere richiamato di nuovo - non vuoi farlo.

La pulizia delle risorse è esattamente ciò che è per il metodo finalize, quindi dovresti essere bravo lì. Un paio di avvertimenti, tuttavia:

Impossibile chiamare il metodo. Se sono state collegate risorse che non verranno automaticamente liberate quando il programma si interrompe, non dipende da finalize.

Quando il metodo è chiamato non è garantito. Con la memoria stretta, questo sarà più presto. Con molta memoria libera, sarà più tardi se non del tutto. Questo potrebbe andar bene: con molta memoria potresti non essere così preoccupato di liberare le risorse. (Anche se appeso su di loro può interferire con altri software in esecuzione allo stesso tempo, nel qual caso si sarebbe essere preoccupati.)

La mia soluzione ususal è quello di avere un qualche tipo di smaltire metodo che fa la pulizia . Lo chiamo esplicitamente ad un certo punto se posso, e non appena posso. Quindi aggiungo un metodo finalize che chiama semplicemente il metodo dispose. (Si noti che il smaltire metodo deve comportarsi bene quando quando viene chiamato più di una volta! In effetti, con questo tipo di programmazione che potrei chiamare smaltire più volte al di fuori finalize, non essendo sicuro se le chiamate precedenti sono state fatte con successo e tuttavia vogliono essere chiamato efficacemente il prima possibile). Ora, idealmente, le mie risorse vengono liberate non appena non ne ho più bisogno.Tuttavia, se perdo la traccia dell'oggetto con le risorse, il metodo finalize mi scaricherà quando la memoria si esaurirà e ho bisogno dell'aiuto.

5

finalize() viene chiamato da Java Garbage Collector quando rileva che non esiste alcun riferimento a quell'oggetto particolare. finalize() è ereditato da tutti gli oggetti Java attraverso la classe Object.

Per quanto ne so non avreste alcun metodo statico difficoltà a fare chiamate da un metodo Finalize() I e si potrebbe stabilire un nuovo riferimento ad esso da finalizzare() - tuttavia direi che questo è scarsa pratica di programmazione.

Non si deve fare affidamento su finalize() per la cancellazione, ed è meglio chiarire man mano che si procede. Preferisco usare try, catch, finalmente per chiarire, piuttosto che usare finalize(). In particolare, utilizzando finalize(), la JVM si terrà su tutti gli altri oggetti a cui fa riferimento l'oggetto finalizzabile, nel caso in cui effettui chiamate su di essi. Ciò significa che ti stai aggrappando alla memoria che potresti non aver bisogno di usare. Ancora più importante, questo significa anche che puoi fare in modo che la JVM non finisca mai nello smaltimento degli oggetti, perché devono trattenerli in un altro oggetto, mentre il metodo finalize ne ha bisogno, ad es. una condizione di gara.

Inoltre, considera che è del tutto possibile che GC non venga chiamato. Pertanto non è possibile in realtà la garanzia che finalize() sarà mai chiamato.

Cancella le risorse come e quando hai finito con loro, e non affidarti a finalize() per farlo è il mio consiglio.

1

Prima di tutto, ricorda che non è possibile garantire che la finalizzazione venga eseguita per tutti gli oggetti. È possibile utilizzarlo per liberare la memoria allocata nel codice nativo associato a un oggetto, ma per il puro codice Java la maggior parte dei casi d'uso serve solo a eseguire un meccanismo di "backup" per ripulire le risorse. Ciò significa che nella maggior parte dei casi dovresti liberare le risorse manualmente ei finalizzatori potrebbero agire solo come una sorta di aiuto da ripulire se ti dimentichi di farlo nel modo standard. Tuttavia, non è possibile utilizzarli come unico o principale meccanismo di pulizia. Ancora più in generale, non dovresti scrivere alcun codice la cui correttezza dipenda dai finalizzatori in esecuzione.

Annuncio 1.Per quanto ne so, non ci sono garanzie su ciò che chiama thread finalize(), anche se in pratica questo sarà probabilmente uno dei thread GC.

Annuncio 2. L'istanziazione di nuovi oggetti è consentita. Tuttavia, ci sono una serie di problemi con la gestione dei riferimenti agli oggetti nei finalizzatori. In particolare, se si memorizza un riferimento hard all'oggetto finalizzato in alcuni oggetti live, è possibile impedire che l'oggetto about-to-be-garbage-gather venga ripulito. Questo tipo di resurrezione di oggetti può portare all'esaurimento delle risorse se diventa fuori controllo. Inoltre, fai attenzione alle eccezioni in finalize() - potrebbero interrompere la finalizzazione, ma non c'è modo automatico per il tuo programma di apprenderle. È necessario avvolgere il codice nei blocchi try-catch e propagare le informazioni da soli. Inoltre, il tempo di esecuzione lungo dei finalizzatori può causare la coda degli oggetti da creare e consumare molta memoria. Alcuni altri problemi e limitazioni degni di nota sono descritti in this JavaWorld article.

Ad 3. Non ci dovrebbero essere problemi con la chiamata di metodi statici dai finalizzatori.

Ad 4. ​​Come accennato al punto 2, è possibile impedire che un oggetto venga sottoposto a garbage collection (per resuscitarlo) ponendo un riferimento ad esso in un altro oggetto live durante la finalizzazione. Tuttavia, questo è un comportamento complicato e probabilmente non una buona pratica.

Per riassumere, non è possibile fare affidamento sui finalizzatori per ripulire le risorse. È necessario gestirlo manualmente e i finalizzatori nel tuo caso potrebbero essere utilizzati al meglio come meccanismo di backup per coprire dopo una codifica scadente in un certo grado. Questo significa, sfortunatamente, che la tua idea di rendere l'API più carina usando i finalizzatori per ripulire le risorse OpenGL probabilmente non funzionerà.