Domanda

Nella prospettiva .NET:

  • Cos'è un perdita di memoria?
  • Come puoi determinare se la tua applicazione perde?Quali sono gli effetti?
  • Come è possibile evitare una perdita di memoria?
  • Se la tua applicazione presenta una perdita di memoria, questa scompare quando il processo termina o viene interrotto?Oppure le perdite di memoria nell'applicazione influiscono su altri processi nel sistema anche dopo il completamento del processo?
  • E che dire del codice non gestito a cui si accede tramite COM Interop e/o P/Invoke?
È stato utile?

Soluzione

La migliore spiegazione che ho visto è nel capitolo 7 del file free e-book Fondamenti di programmazione.

Fondamentalmente, dentro .NETTO si verifica una perdita di memoria quando gli oggetti a cui si fa riferimento sono rootati e quindi non possono essere sottoposti a Garbage Collection.Ciò si verifica accidentalmente quando si mantengono riferimenti oltre l'ambito previsto.

Saprai di avere delle perdite quando inizierai a ricevere OutOfMemoryExceptions o l'utilizzo della memoria va oltre quanto ti aspetteresti (PerfMon ha dei bei contatori di memoria).

Comprensione .NETTOIl modello di memoria di è il modo migliore per evitarlo.Nello specifico, per capire come funziona il garbage collector e come funzionano i riferimenti, vi rimando ancora una volta al capitolo 7 dell'e-book.Inoltre, fai attenzione alle insidie ​​​​più comuni, probabilmente le più comuni sono gli eventi.Se oggetto UN è registrato a un evento sull'oggetto B, quindi obiettare UN resterà in giro fino all'oggetto B scompare perché B contiene un riferimento a UN.La soluzione è annullare la registrazione dei tuoi eventi quando hai finito.

Naturalmente, un buon profilo di memoria ti consentirà di vedere i grafici degli oggetti ed esplorare la nidificazione/riferimento dei tuoi oggetti per vedere da dove provengono i riferimenti e quale oggetto root è responsabile (Profilo delle formiche dal cancello rosso, Memoria punto JetBrains, memprofiler sono davvero ottime scelte, oppure puoi usare solo testo WinDbg E sos, ma consiglio vivamente un prodotto commerciale/visivo a meno che tu non sia un vero guru).

Credo che il codice non gestito sia soggetto alle tipiche perdite di memoria, tranne per il fatto che i riferimenti condivisi sono gestiti dal garbage collector.Potrei sbagliarmi su quest'ultimo punto.

Altri suggerimenti

In senso stretto, una perdita di memoria consuma memoria che "non viene più utilizzata" dal programma.

"Non più utilizzato" ha più di un significato, potrebbe significare "nessun riferimento ad esso", cioè totalmente irrecuperabile, oppure potrebbe significare referenziato, recuperabile, inutilizzato ma il programma mantiene comunque i riferimenti.Solo quest'ultimo si applica a .Net for oggetti perfettamente gestiti.Tuttavia, non tutte le classi sono perfette e, a un certo punto, un'implementazione non gestita sottostante potrebbe perdere risorse in modo permanente per tale processo.

In tutti i casi, l'applicazione consuma più memoria di quella strettamente necessaria.Gli effetti collaterali, a seconda della quantità trapelata, potrebbero andare da nessuno, al rallentamento causato da una raccolta eccessiva, a una serie di eccezioni di memoria e infine a un errore fatale seguito dalla chiusura forzata del processo.

Sai che un'applicazione ha un problema di memoria quando il monitoraggio mostra che sempre più memoria è allocata al tuo processo dopo ogni ciclo di raccolta dei rifiuti.In tal caso, stai mantenendo troppa memoria oppure si stanno verificando perdite di informazioni da parte di un'implementazione non gestita sottostante.

Per la maggior parte delle perdite, le risorse vengono recuperate al termine del processo, tuttavia alcune risorse non vengono sempre recuperate in alcuni casi precisi, gli handle del cursore GDI sono noti per questo.Naturalmente, se si dispone di un meccanismo di comunicazione tra processi, la memoria allocata nell'altro processo non verrebbe liberata finché il processo non la libera o non termina.

Penso che alle domande "cos'è una perdita di memoria" e "quali sono gli effetti" sia già stata data una buona risposta, ma volevo aggiungere qualche altra cosa sulle altre domande...

Come capire se la tua applicazione perde

Un modo interessante è aprire perfmon e aggiungi tracce per # byte in tutti gli heap E # Collezioni di seconda generazione , in ogni caso guardando solo il tuo processo.Se l'esercizio di una particolare funzionalità provoca un aumento dei byte totali e la memoria rimane allocata dopo la successiva raccolta di seconda generazione, si potrebbe dire che la funzionalità perde memoria.

Come prevenire

Sono stati espressi altri buoni pareri.Vorrei solo aggiungere che forse il più comunemente trascurato La causa delle perdite di memoria .NET è l'aggiunta di gestori eventi agli oggetti senza rimuoverli.Un gestore eventi collegato a un oggetto è una forma di riferimento a quell'oggetto, quindi impedirà la raccolta anche dopo che tutti gli altri riferimenti sono stati eliminati.Ricordarsi sempre di scollegare i gestori di eventi (utilizzando il file -= sintassi in C#).

La perdita scompare quando il processo termina e per quanto riguarda l'interoperabilità COM?

Quando il processo termina, tutta la memoria mappata nel suo spazio di indirizzi viene recuperata dal sistema operativo, inclusi eventuali oggetti COM serviti dalle DLL.Relativamente raramente, gli oggetti COM possono essere serviti da processi separati.In questo caso, quando il processo termina, potresti essere ancora responsabile della memoria allocata in qualsiasi processo del server COM utilizzato.

Definirei le perdite di memoria come un oggetto che non libera tutta la memoria allocata dopo il suo completamento.Ho scoperto che questo può accadere nella tua applicazione se utilizzi API e COM di Windows (ad es.codice non gestito che contiene un bug o non viene gestito correttamente), nel framework e in componenti di terze parti.Ho anche scoperto che il problema non può essere riordinato dopo aver utilizzato determinati oggetti come le penne.

Personalmente ho riscontrato eccezioni di memoria insufficiente che possono essere causate ma non sono esclusive di perdite di memoria nelle applicazioni dot net.(OOM può anche provenire da pinning see Bloccare Artical).Se non ricevi errori OOM o hai bisogno di confermare se si tratta di una perdita di memoria che lo causa, l'unico modo è profilare la tua applicazione.

Vorrei anche provare a garantire quanto segue:

a) Tutto ciò che implementa Idisposable viene eliminato utilizzando un blocco final o l'istruzione using, inclusi pennelli, penne, ecc. (alcune persone sostengono di impostare tutto su zero in aggiunta)

b) Tutto ciò che ha un metodo close viene chiuso di nuovo utilizzando finalmente o l'istruzione using (anche se ho scoperto che using non sempre si chiude a seconda se hai dichiarato l'oggetto al di fuori dell'istruzione using)

c)Se si utilizzano codice/API Windows non gestiti, questi verranno gestiti correttamente in seguito.(alcuni hanno metodi di pulizia per rilasciare risorse)

Spero che questo ti aiuti.

Se è necessario diagnosticare una perdita di memoria in .NET, controllare questi collegamenti:

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

Questi articoli descrivono come creare un dump della memoria del processo e come analizzarlo in modo da poter determinare innanzitutto se la perdita di dati non è gestita o gestita e, se è gestita, come capire da dove proviene.

Microsoft dispone anche di uno strumento più recente per assistere nella generazione di dump di arresti anomali, per sostituire ADPlus, chiamato DebugDiag.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en

Utilizzo di CLR Profiler di Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en è un ottimo modo per determinare quali oggetti contengono memoria, quale flusso di esecuzione porta alla creazione di questi oggetti e anche monitorare quali oggetti risiedono in quale posizione nell'heap (frammentazione, LOH, ecc.).

La migliore spiegazione di come funziona il garbage collector si trova in Jeff Richters CLR tramite C# libro, (cap.20).Leggerlo fornisce un'ottima base per comprendere come gli oggetti persistono.

Una delle cause più comuni di rooting accidentale di oggetti è il collegamento di eventi esterni a una classe.Se colleghi un evento esterno

per esempio.

SomeExternalClass.Changed += new EventHandler(HandleIt);

e dimentica di sganciarlo quando lo smaltisci, SomeExternalClass ha un riferimento alla tua classe.

Come accennato in precedenza, il Profilatore di memoria SciTech è eccellente per mostrarti le radici degli oggetti che sospetti abbiano perdite.

Ma c'è anche un modo molto rapido per verificare un tipo particolare è semplicemente usare WnDBG (puoi anche usarlo nella finestra immediata di VS.NET mentre sei collegato):

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

Ora fai qualcosa che pensi possa eliminare gli oggetti di quel tipo (ad es.chiudere una finestra).In questo caso è utile avere da qualche parte un pulsante di debug che venga eseguito System.GC.Collect() un paio di volte.

Allora corri !dumpheap -stat -type <TypeName> Ancora.Se il numero non è sceso, o non è sceso quanto previsto, allora hai una base per ulteriori indagini.(Ho ricevuto questo suggerimento da un seminario tenuto da Ingo Rammer).

Immagino che in un ambiente gestito, una perdita sarebbe se mantenessi in giro un riferimento non necessario a una grande porzione di memoria.

Perché le persone pensano che una perdita di memoria in .NET non sia uguale a qualsiasi altra perdita?

Una perdita di memoria avviene quando ci si collega a una risorsa e non la si lascia andare.Puoi farlo sia nella codifica gestita che in quella non gestita.

Per quanto riguarda .NET e altri strumenti di programmazione, sono state avanzate idee sulla raccolta dei rifiuti e altri modi per ridurre al minimo le situazioni che potrebbero causare perdite di dati nell'applicazione.Ma il metodo migliore per prevenire perdite di memoria è che sia necessario comprendere il modello di memoria sottostante e come funzionano le cose sulla piattaforma che si sta utilizzando.

Credere che GC e altre magie risolveranno il tuo pasticcio è la via più breve per perdite di memoria e sarà difficile da trovare in seguito.

Quando codifichi non gestito, normalmente ti assicuri di ripulire, sai che le risorse di cui ti impossessi saranno tua responsabilità di ripulire, non quella del custode.

In .NET, invece, molte persone pensano che il GC ripulirà tutto.Bene, fa qualcosa per te, ma devi assicurarti che sia così..NET racchiude molte cose, quindi non sempre sai se hai a che fare con una risorsa gestita o non gestita e devi assicurarti di cosa hai a che fare.La gestione dei caratteri, delle risorse GDI, della directory attiva, dei database ecc. è in genere una cosa a cui devi prestare attenzione.

In termini gestiti mi metterò il collo sulla linea per dire che scompare una volta che il processo viene ucciso/rimosso.

Vedo che molte persone soffrono di questo problema e spero davvero che tutto questo finisca.Non puoi chiedere all'utente di terminare la tua app per ripulire il pasticcio!Dai un'occhiata a un browser, che può essere IE, FF ecc., quindi apri, ad esempio, Google Reader, lascialo riposare per alcuni giorni e guarda cosa succede.

Se poi apri un'altra scheda nel browser, navighi su qualche sito, quindi chiudi la scheda che ospitava l'altra pagina che ha causato la perdita del browser, pensi che il browser rilascerà la memoria?Non così con IE.Sul mio computer IE consumerà facilmente 1 GiB di memoria in un breve lasso di tempo (circa 3-4 giorni) se utilizzo Google Reader.Alcune pagine di notizie sono anche peggio.

Immagino in un ambiente gestito, una perdita sarebbe tu mantenendo un riferimento inutile a un grosso pezzo di memoria in giro.

Assolutamente.Inoltre, il mancato utilizzo del metodo .Dispose() su oggetti usa e getta quando appropriato può causare perdite di memoria.Il modo più semplice per farlo è con un blocco using perché esegue automaticamente .Dispose() alla fine:

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

E se crei una classe che utilizza oggetti non gestiti, se non stai implementando IDisposable correttamente, potresti causare perdite di memoria per gli utenti della tua classe.

Tutte le perdite di memoria vengono risolte terminando il programma.

Perdita di memoria sufficiente e il sistema operativo potrebbe decidere di risolvere il problema per tuo conto.

Concordo con Bernard su cosa sarebbe una perdita di mem in .net.

Potresti profilare la tua applicazione per vedere il suo utilizzo della memoria e determinare che se gestisce molta memoria quando non dovrebbe esserlo, potresti dire che ha una perdita.

In termini gestiti, metterò il collo in gioco per dire che scompare una volta che il processo viene ucciso/rimosso.

Il codice non gestito è una bestia a sé stante e se esiste una perdita al suo interno, seguirà un mem.definizione di perdita.

Tieni inoltre presente che .NET ha due heap, uno dei quali è l'heap degli oggetti di grandi dimensioni.Credo che oggetti di circa 85k o più vengano inseriti in questo heap.Questo heap ha regole di durata diverse rispetto all'heap normale.

Se stai creando strutture di memoria di grandi dimensioni (dizionari o elenchi) sarebbe prudente andare a cercare quali sono le regole esatte.

Per quanto riguarda il recupero della memoria al termine del processo, a meno che non si utilizzi Win98 o un suo equivalente, tutto viene rilasciato nuovamente al sistema operativo al termine.Le uniche eccezioni sono gli elementi aperti tra più processi e un altro processo ha ancora la risorsa aperta.

Gli oggetti COM possono essere complicati però.Se usi sempre il IDispose modello, sarai al sicuro.Ma mi sono imbattuto in alcuni assembly di interoperabilità che implementano IDispose.La chiave qui è chiamare Marshal.ReleaseCOMObject quando hai finito.Gli oggetti COM utilizzano ancora il conteggio dei riferimenti COM standard.

ho trovato Profilo di memoria .Net un ottimo aiuto quando si trovano perdite di memoria in .Net.Non è gratuito come Microsoft CLR Profiler, ma secondo me è più veloce e più pertinente.UN

Una definizione è: Impossibile rilasciare memoria non raggiungibile, che non può più essere allocata al nuovo processo durante l'esecuzione del processo di allocazione.Nella maggior parte dei casi può essere curata utilizzando tecniche GC o rilevata tramite strumenti automatizzati.

Per maggiori informazioni per favore visita http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top