2012-07-21 10 views
5

Abbiamo una sorta di perdita, la cui natura non capisco. Gli heap di Gen0/1/2 non aumentano di dimensioni, tuttavia il Working Set aumenta fino a OOM.Finalizer Coda in crescita ma Managed Heaps non

DebugDiag mi dice che CLR.DLL possiede la memoria crescente e mi dice anche che abbiamo una coda crescente di finalizzatori - 100s di migliaia di oggetti Texture2D (è un'app XNA) che aumentano con il tempo .. Tuttavia nessun profiler (dotTrace , Ants, CLR Profiler) possono trovare questi oggetti - non vengono visualizzati nell'heap e le attestazioni CLRProfiler non vengono mai assegnati.

Quindi guardo in WinDbg - ancora una volta vedo una coda di Finalizer in crescita piena di Texture2D. fReachable è vuoto e afferma che tutti gli oggetti sono comunque nello heap.

*0:038> !finalizequeue 
SyncBlocks to be cleaned up: 0 
MTA Interfaces to be released: 0 
STA Interfaces to be released: 0 
---------------------------------- 
generation 0 has 1881 finalizable objects (33e365b0->33e38314) 
generation 1 has 41580 finalizable objects (33e0dc00->33e365b0) 
generation 2 has 685816 finalizable objects (33b70020->33e0dc00) 
Ready for finalization 0 objects (33e38314->33e38314) 
     MT Count TotalSize Class Name 
......snip...... 
00ce67e0 726827  49424236 Microsoft.Xna.Framework.Graphics.Texture2D* 

Quindi cerco quelle 726.000 istanze in modo che possa trovare chi le possiede. Il problema è che dumpheap dice che c'è solo 218. Questo è più o meno quello che mi aspetto e ciò che i profiler gestiti mi dicono che esiste.

*0:038> !dumpheap -stat -type Microsoft.Xna.Framework.Graphics.Texture2D 
total 0 objects 
Statistics: 
     MT Count TotalSize Class Name 
00ce67e0  218  14824 Microsoft.Xna.Framework.Graphics.Texture2D 
Total 218 objects* 

Quindi da dove vengono il resto degli elementi nella coda del finalizzatore? In questo momento ho il sospetto che la crescente coda del finalizzatore per le allocazioni di memoria man mano che cresce e quindi esce OOM. È come se quei 218 elementi vengano aggiunti alla coda di Finalizer più volte per qualche motivo.

Molte grazie

Andy

+0

Si menzionano gli heap gen0/1/2, ma per quanto riguarda l'heap degli oggetti grandi? È stabile? Cosa intendono le persone in generale per "dimensione dell'heap"? Sono le dimensioni combinate di tutti gli oggetti di quell'heap o la dimensione della memoria riservata per l'heap? Se la frammentazione è elevata, questi due valori possono divergere. – Joh

+0

Si scusa per non aver detto che il LOH è anche molto stabile come lo sono i byte in tutti i contatori di heap. In generale sto parlando dei contatori perfmon per le taglie. Quando ho guardato il layout dell'indirizzo dal profiler CLR, il LOH non è molto frammentato e la sua dimensione complessiva non sta crescendo. Ma tornerò a controllare. Grazie –

risposta

2

Per completezza ecco cosa stava succedendo.

C'è un problema con XNA che causa il richiamo di ReRegisterForFinalize su Texture2D se si accede alla collezione Device.Texture []. Sunburn accede a questa raccolta ogni frame per aggirare un altro problema di XNA, quindi se hai un sacco di trame, questo può accumularsi molto rapidamente.

Ci sono diversi punti in XNA (Texture3D, ecc.) Che usano lo stesso schema, quindi presumo che sia possibile riproporlo in molti modi.

Poiché non c'è alcun segno di XNA 5, abbiamo dovuto aggirare il problema: ho scoperto che semplicemente chiamando SuppressFInalize dopo aver effettuato l'accesso alla raccolta, è stato rimosso l'elemento aggiuntivo che era stato aggiunto. Pensiamo che sia sicuro :-) I ragazzi di Sunburn stanno aggiungendo una correzione nel loro codice e speriamo che il problema scompaia.

1

Potrebbe essere la stessa istanza di essere ri-aggiunti alla coda di finalizzazione? Dai documenti su Object.Finalize:

Finalize viene chiamato automaticamente solo una volta su una determinata istanza, a meno che l'oggetto non venga registrato nuovamente utilizzando un meccanismo come GC.ReRegisterForFinalize e GC.SuppressFinalize non sia stato successivamente chiamato.

Questa è l'unica spiegazione che sembra corrispondere a quello che stai vedendo qui. Non sono sicuro del motivo per cui sarebbe stato registrato nuovamente per la finalizzazione.

+0

Sì, potrebbe essere. Ho avuto un paio di suggerimenti che questa potrebbe essere la causa. Tuttavia, nessuno del codice che ho come sorgente utilizza GC.ReRegisterForFinalize in modo da rendere difficile il debug. So che l'ultima versione di. Reflector di .Net ti permette di fare breakpoint su linee di codice all'interno di binari, così potrei provarlo. Il mio voodoo WinDbg non è abbastanza forte da farlo nel modo più duro. –