Stavo leggendo l'articolo this l'altro giorno e mi chiedevo perché c'era un Finalizzatore insieme al metodo Dispose. Ho letto here in SO in merito al motivo per cui è possibile aggiungere Dispose al Finalizer. La mia curiosità è, quando sarebbe chiamato il Finalizer sul metodo Dispose stesso? Esiste un esempio di codice o si basa su qualcosa che accade sul sistema in cui è in esecuzione il software? In tal caso, cosa potrebbe accadere di non avere il metodo Dispose eseguito dal GC.Quando non viene richiamato il metodo?
risposta
Lo scopo del finaliser qui è semplicemente una misura di sicurezza contro le perdite di memoria (se vi capita non chiamare Dispose
esplicitamente). Significa anche che non è necessario disporre gli oggetti se si desidera che rilascino risorse quando il programma si arresta, poiché il GC sarà costretto a finalizzare e raccogliere comunque tutti gli oggetti.
Come punto correlato, è importante disporre l'oggetto in modo leggermente diverso quando si esegue questa operazione dal finalizzatore.
~MyClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(disposing)
{
if (!this.disposed)
{
if (disposing)
{
// Dispose managed resources here.
}
// Dispose unmanaged resources here.
}
this.disposed = true;
}
La ragione per cui non non vogliono smaltire le risorse gestite in finaliser è che si sarebbe in realtà essere la creazione di forti riferimenti a loro in questo modo, e questo potrebbe impedire al GC di fare il suo lavoro correttamente e la raccolta loro. Le risorse non gestite (ad esempio handle Win32 e simili) devono sempre essere esplicitamente chiuse/eliminate, ovviamente, poiché CLR non ne è a conoscenza.
Questo è principalmente lì per proteggersi. Non puoi dettare ciò che l'utente finale della tua classe farà. Fornendo un finalizzatore oltre a un metodo Dispose, il GC "Dispose" del tuo oggetto, liberando le risorse in modo appropriato, anche se l'utente dimentica di chiamare Dispose() o usa male la tua classe.
Vale la pena ricordare che il GC non è deterministico, quindi non c'è alcuna garanzia di quando, * o anche se *, il tuo finalizzatore verrà chiamato. – LukeH
Sì - Se il programma viene eseguito abbastanza a lungo, è più probabile che il tuo oggetto venga finalizzato. Inoltre, se si spegne in modo pulito, verrà finalizzato. Ma non ci sono garanzie con il GC - che è parte del perché IDisposable esiste in primo luogo. –
Il metodo di eliminazione deve essere chiamato in modo esplicito, chiamando Dispose() o avendo l'oggetto in un'istruzione using. Il GC chiamerà sempre il finalizzatore, quindi se c'è qualcosa che deve accadere prima che gli oggetti siano eliminati, il finalizzatore dovrebbe almeno controllare per assicurarsi che tutto nell'oggetto venga ripulito.
Si vuole evitare di pulire gli oggetti nel finalizzatore se possibile, perché provoca un lavoro extra rispetto al loro smaltimento prima della mano (come la chiamata di smaltimento), ma si dovrebbe sempre controllare il finalizzatore se ci sono oggetti disteso che deve essere rimosso.
Il Finalizer viene chiamato quando l'oggetto viene sottoposto a garbage collection. Smaltire deve essere chiamato esplicitamente. Nel codice seguente verrà chiamato il finalizzatore, ma il metodo Dispose non lo è.
class Foo : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed");
}
~Foo()
{
Console.WriteLine("Finalized");
}
}
...
public void Go()
{
Foo foo = new Foo();
}
Questo non è completamente vero. Il finalizzatore viene chiamato qualche tempo dopo che l'oggetto sarebbe altrimenti idoneo per la garbage collection (cioè l'applicazione non fa più riferimento all'istanza). Tuttavia, poiché il finalizzatore deve essere eseguito per l'istanza, il CLR effettivamente radica l'oggetto e quindi non viene raccolto in modo automatico finché non viene eseguito il finalizzatore. –
Inoltre, non c'è alcuna garanzia che un oggetto * sarà * mai GC'd o che il suo finalizzatore sarà * mai * chiamato. Ecco perché è doppiamente importante assicurarsi di smaltire correttamente qualsiasi oggetto 'IDisposable'. – LukeH
Una nota importante ma sottile non ancora citato: uno scopo raramente considerata di smaltimento è impedire che un oggetto venga ripulito prematuramente. Gli oggetti con finalizzatori devono essere scritti attentamente, per evitare che un finalizzatore venga eseguito in precedenza del previsto. Un finalizzatore non può essere eseguito prima dell'inizio dell'ultima chiamata al metodo che verrà eseguita su un oggetto (*), ma potrebbe talvolta eseguire durante l' l'ultima chiamata al metodo se l'oggetto verrà abbandonato una volta completato il metodo. Il codice che disponga correttamente un oggetto non può abbandonare l'oggetto prima di chiamare Dispose, quindi non vi è alcun pericolo che un finalizzatore causi il caos sul codice che usa correttamente Dispose. D'altra parte, se l'ultimo metodo per utilizzare un oggetto fa uso di entità che verranno ripulite nel finalizzatore dopo l'ultimo utilizzo del riferimento all'oggetto stesso, è possibile che il garbage-collector chiami Finalize sull'oggetto e pulisca su entità che sono ancora in uso.Il rimedio è quello di garantire che qualsiasi metodo di chiamata che utilizza entità che verranno ripulite da un finalizzatore debba essere seguito ad un certo punto da una chiamata al metodo che fa uso di "questo". GC.KeepAlive (questo) è un buon metodo da usare per questo.
(*) I metodi non virtuali che sono estesi al codice in linea che non fa nulla con l'oggetto possono essere esenti da questa regola, ma Dispose di solito è, o invoca, un metodo virtuale.
- 1. Perché non viene richiamato il metodo sovraccaricato?
- 2. Quando viene richiamato Move Constructor?
- 3. Perché copiare il costruttore non viene richiamato?
- 4. In Go, perché il mio metodo di interfaccia Stringer non viene richiamato? Quando si utilizza fmt.Println
- 5. Come applicare una chiamata al metodo (nella classe base) quando viene richiamato il metodo di sovrascrittura?
- 6. Quando viene richiamato l'operatore == di Double?
- 7. il metodo angularjs ng-class viene richiamato più volte
- 8. Hibernate @PostLoad non viene mai richiamato
- 9. perché TestInitialize non viene richiamato automaticamente?
- 10. iOS 7 didEnterRegion non viene richiamato affatto
- 11. iPhone: perché non viene richiamato drawRect?
- 12. cellForItemAtIndexPath, sizeForItemAtIndexPath, insetForSectionAtIndex non viene richiamato iOS
- 13. Il validatore personalizzato JSF non viene richiamato quando il valore di input inviato è nullo
- 14. Perché non viene richiamato il mio callback jQuery.get()?
- 15. Spring Aspect non riesce quando il punto di join viene richiamato nella nuova discussione
- 16. AngularJS - il metodo di controllo non viene richiamato su ngClick - nessun errore
- 17. onSaveInstanceState non viene richiamato dopo la rotazione dello schermo
- 18. Quando il BroadcastReceiver viene richiamato tramite un Intent, su quale processo viene eseguito?
- 19. Quando viene chiamato il metodo IEnumerator.Reset()?
- 20. PHPUnit - Come verificare se viene richiamato il callback?
- 21. Quando viene chiamato il metodo layoutSubviews?
- 22. Il metodo onPreferenceChange non viene chiamato quando si modifica Listpreferences
- 23. Il metodo senza argomento su window.external viene richiamato durante il controllo con typeof
- 24. Il comando contenitore non può essere richiamato
- 25. Android - onTabChanged non viene richiamato selezionando una scheda diversa
- 26. onTouchEvent() non sarà attivato se setSystemUiVisibility (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) viene richiamato
- 27. Il metodo MsTest TestCleanup non viene chiamato quando viene generata un'eccezione non gestita
- 28. Il metodo Mailer non viene chiamato?
- 29. Metodo richiamato dopo il rilascio Eccezione Anteprima fotocamera
- 30. Ajax.BeginForm OnFailure richiamato quando ModelState è InValid
E un altro motivo per non disporre di risorse gestite nel finalizzatore ... È possibile che potrebbero essere già stati convertiti in GC nel momento in cui viene eseguito il finalizzatore. Provare a smaltirli quando sono già stati raccolti causerebbe un errore di runtime. – LukeH
@Luke: true, ma è possibile evitarlo abbastanza facilmente impostando tutti i riferimenti su null e quindi eseguendo un controllo nullo prima dello smaltimento. – Noldorin
@Noldorin - Dove si annulla l'annullamento della registrazione degli eventi nel tuo esempio? Capisco che tecnicamente sarebbero stati gestiti, ma se avessimo qualche oggetto legato a questa classe attraverso un evento e non lo annetteremo alla registrazione nella parte non gestita (supponendo che l'utente non chiami Dispose direttamente e sia lasciato al GC per pulirlo) su). Sarebbe sicuro/ok mettere l'annullamento della registrazione degli eventi nella sezione gestita per assicurarsi che ciò accada? L'effetto collaterale potrebbe essere che qualcuno pensa di eliminare un oggetto, ma in realtà non viene mai eliminato a causa del collegamento tra questa classe e l'altro. – SwDevMan81