2009-08-12 11 views
6

Ho un'applicazione WindowsForms che sembra perdere memoria, quindi ho usato ANTS Memory Profiler di Redgate per esaminare gli oggetti che sospetto e scoprire che sono trattenuti solo da oggetti già nella coda Finalizer. Fantastico, esattamente cos'è la coda di Finalizer? Puoi indicarmi la migliore definizione? Puoi condividere qualche consiglio aneddotico?Quali sono Finalization Queue e Control + ThreadMethodEntry?

Inoltre, tutti gli oggetti GC radice nella coda Finalizer sono istanze di oggetti System.Windows.Forms.Control + ThreadMethodEntry denominati "chiamante". Vedo che è coinvolto nell'interazione dell'interfaccia multi-thread, ma non ne so molto oltre. Perdona la mia apparente pigrizia e ammetto l'ignoranza, ma queste risorse sono tutte sepolte all'interno della componente di un venditore. Sto parlando al venditore di questi problemi, ma ho bisogno di qualche direzione per aggiornarmi sulla conversazione. Puoi indicarmi anche la definizione più utile di ThreadMethodEntry? Qualche consiglio aneddotico?

Inoltre, dovrei preoccuparmi anche di questi oggetti sulla coda del finalizzatore?

Aggiornamento: Questo Red Gate article è stato utile.

risposta

14

La coda del finalizzatore contiene tutti gli oggetti con un metodo finalizzatore definito. Ricorda che un finalizzatore è un mezzo per raccogliere risorse non gestite come i manici. Quando il garbage collector raccoglie garbage, sposta qualsiasi oggetto con un finalizzatore nella coda del finalizzatore. A un certo punto più tardi, a seconda della pressione della memoria, dell'euristica GC e della fase lunare, quando il garbage collector decide di raccogliere questi oggetti, cammina lungo la coda e esegue i finalizzatori.

Avendo lavorato con perdite di memoria in passato, vedere un mucchio di oggetti del fornitore nella coda del finalizzatore potrebbe essere un codice sciatto, ma non indica una perdita di memoria. In genere, un buon codice esporrà un metodo Dispose che raccoglierà risorse gestite e non gestite e, in tal modo, rimuoverà dalla coda del finalizzatore tramite GC.SuppressFinalize(). Quindi, se gli oggetti del fornitore implementano un metodo Dispose e il tuo codice non lo chiama, ciò potrebbe portare a un sacco di oggetti nella coda del finalizzatore.

Hai provato a creare un'istantanea in ANTS tra due punti nel tempo e confrontando gli oggetti creati tra loro? Questo può aiutarti a identificare eventuali oggetti gestiti che sono trapelati.

Inoltre, se si vuole vedere se la memoria va via quando i finalizzatori vengono eseguiti, provate questo solo per testare con:

 
System.GC.Collect(); 
System.GC.WaitForPendingFinalizers(); // this method may block while it runs the finalizers 
System.GC.Collect(); 

Non consiglio di eseguire il codice normalmente. Potresti eseguirlo se hai fatto un sacco di lavoro e hai creato molti rifiuti. Ad esempio, nella nostra app, una delle nostre funzioni può creare circa 350 MB di rifiuti inutili dopo aver chiuso una finestra MDI. Dato che questo è noto per lasciare un sacco di spazzatura, forziamo manualmente la raccolta dei rifiuti.

Si noti inoltre che esiste una cache di proprietà di basso livello nel codice di base Windows.Forms che manterrà l'ultima finestra di dialogo modale aperta. Questa potrebbe essere una fonte di perdita di memoria. Un modo sicuro per eliminare questo riferimento è forzare la visualizzazione di un'altra semplice finestra di dialogo, quindi eseguire il codice GC sopra riportato.

+0

Grazie per l'ottima risposta, Paul. Questo è il grafico di riferimento dell'oggetto di cui sto parlando, guardando i nuovi oggetti nella seconda istantanea, dopo che le risorse dovrebbero essere pulite. Tutti gli oggetti nel grafico che implementano IDisposable hanno un suggerimento che dice "Dispose() è stato chiamato per questo oggetto" ma l'oggetto selezionato non ha tale tooltip. – flipdoubt

+2

Nota su ThreadMethodEntry: Penso che vengano utilizzati in qualsiasi richiamo al thread dell'interfaccia utente. Ogni oggetto Control ha una coda di callback di thread di tipo ThreadMethodEntry. Un callback rimuove un ThreadMethodEntry e lo esegue. Ogni oggetto ThreadMethodEntry ha una serie di campi interni. Esaminare questi campi potrebbe aiutarti a capire quale di questi oggetti del fornitore sta invocando. Non ricordo se è possibile ottenere informazioni da ANTS, ma so che è possibile tramite WinDbg.dll e sos.dll (estensioni del debugger gestito). Guarda il delegato "metodo" e il controllo "chiamante". –

+0

Inoltre, si noti che gli oggetti ThreadMethodEntry implementano un finalizzatore, ma non hanno un metodo Dispose. Quando si completano, anche loro verrebbero spostati nella coda del finalizzatore. –

1

La coda del finalizzatore è una coda in cui le istanze di oggetto non più utilizzate sono in attesa di essere finalizzate dal GC. Tutti gli oggetti in questa coda saranno finalizzati e le perdite di memoria probabilmente non provengono direttamente da una di queste. Ma uno di questi oggetti potrebbe non rilasciare tutte le sue risorse non gestite.

La classe ThreadMethodEntry è un'implementazione di IAsyncResult e le istanze di questa classe vengono in genere create durante il richiamo di operazioni asincrone, come l'utilizzo di Invoke per aggiornare l'interfaccia utente o utilizzando i metodi Begin */End *.

0

Here's un buon post sul blog che descrive un problema simile. A un livello più tecnico, è possibile utilizzare SOS.dll (descritto nel post del blog) e Sosex.dll per capire perché questi oggetti ThreadMethodEntry sono presenti in memoria. Ci sono comandi in queste estensioni di WinDbg che possono rintracciare quali altri oggetti fanno riferimento a un oggetto specifico in memoria.

Problemi correlati