Vă mulțumim pentru susținere

Anatomia unei "scurgeri de memorie"

În perspectiva .NET:

  • Ce este Leakage de memorie ?
  • Cum puteți determina dacă cererea dumneavoastră scurgeri? Care sunt efectele?
  • Cum puteți preveni o scurgere de memorie?
  • În cazul în care aplicația dvs. prezintă scurgeri de memorie, atunci când procesul se oprește sau este ucis, acesta dispare? Sau scurgeri de memorie în aplicația dvs. afectează alte procese din sistem chiar și după terminarea procesului?
  • Și cum rămâne cu codul neangajat accesat prin COM Interop și / sau P / Invoke?
0
adăugat editat

15 răspunsuri

Aș defini scurgerile de memorie ca un obiect care nu eliberează toată memoria alocată după ce a terminat. Am constatat că acest lucru se poate întâmpla în aplicația dvs. dacă utilizați Windows API și COM (adică un cod care nu este gestionat și care are un bug în acesta sau nu este gestionat corect), în cadrul și în componentele terțelor părți. De asemenea, am descoperit că nu am reușit să folosesc anumite obiecte, cum ar fi pixuri, care pot cauza problema.

Eu personal am suferit excepții de memorie care pot fi cauzate, dar nu sunt exclusive pentru scurgeri de memorie în aplicații net dot. (OOM poate de asemenea să vină de la fixare) Pinning Artical ) . Dacă nu primiți erori OOM sau nu trebuie să confirmați dacă este o scurgere de memorie cauzată de aceasta, atunci singura modalitate este să vă profilați aplicația.

De asemenea, încerc să asigur următoarele:

a) Tot ceea ce pune în aplicare Idisposable este dispus fie folosind un bloc în cele din urmă sau utilizarea declarație acestea includ perii, pixuri etc (unii oameni argumentează pentru a seta totul la nimic în plus)

b) Orice care are o metodă apropiată este închis din nou utilizând în cele din urmă sau folosind instrucțiunea (deși am găsit că nu se închide mereu în funcție dacă ați declarat obiectul în afara instrucțiunii care utilizează)

c) Dacă utilizați coduri / ferestre API care nu sunt administrate, acestea sunt tratate corect după. (unele au metode de curățare pentru a elibera resurse)

Sper că acest lucru vă ajută.

0
adăugat

Cred că într-un mediu gestionat, o scurgere ar fi să păstrați o referință inutilă la o bucată mare de memorie în jur.

0
adăugat

Voi fi de acord cu Bernard ca să nu spun ce ar fi o scurgere de mem.

Aveți posibilitatea să vă profilați aplicația pentru a vedea utilizarea memoriei și să determinați că, dacă gestionarea ei o mulțime de memorie atunci când nu ar trebui să fie ați putea spune că are o scurgere.

În termeni gestionați, îmi voi pune gâtul pe linie pentru a spune că nu mai merge, după ce procesul este ucis / eliminat.

Codul neangajat este propria fiară și dacă există o scurgere în el, va urma un mem standard. Definiție de scurgere.

0
adăugat

Cred că într-un mediu gestionat, a   scurgeri ar fi să păstrați un   referință inutilă la o bucată mare   de memorie în jur.

Absolut. De asemenea, nefolosirea metodei .Dispunere () pe obiecte de unică folosință, dacă este cazul, poate provoca scurgeri de mem. Cel mai simplu mod de a face acest lucru este cu un bloc de utilizare, deoarece execută automat .Dispozitiv () la sfârșit:

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

Și dacă creați o clasă care utilizează obiecte nesupravegheate, dacă nu implementați ID-ul corect, ați putea provoca scurgeri de memorie pentru utilizatorii din clasa ta.

0
adăugat

Strict vorbind, o scurgere de memorie consumă memorie care nu mai este utilizată de program.

"Nu mai este folosit" are mai mult de un sens, ar putea însemna "nu mai face referire la acesta", adică total nerecuperabil sau ar putea însemna referință, recuperabilă, neutilizată, dar programul păstrează referințele oricum. Doar ultima se aplică pentru .Net pentru obiecte perfect gestionate . Cu toate acestea, nu toate clasele sunt perfecte și, la un moment dat, o implementare neadministrată subiacentă ar putea duce la scurgerea resurselor în permanență pentru acest proces.

În toate cazurile, aplicația consumă mai multă memorie decât este strict necesară. Efectele laterale, în funcție de suma scursă, s-ar putea schimba de la zero, la încetinirea datorată colectării excesive, la o serie de excepții de memorie și, în final, la o eroare fatală urmată de terminarea procesului forțat.

Știți că o aplicație are o problemă de memorie atunci când monitorizarea arată că mai multă memorie este alocată procesului dvs. după fiecare ciclu de colectare a gunoiului . În acest caz, fie păstrați prea mult în memorie, fie unele implementări neadministrate subiacente sunt scurgeri.

Pentru cele mai multe scurgeri, resursele sunt recuperate atunci când procesul este terminat, cu toate că unele resurse nu sunt întotdeauna recuperate în anumite cazuri precise, mânerele cursor GDI sunt notorii pentru asta. Desigur, dacă aveți un mecanism de comunicare interproces, memoria alocată în celălalt proces nu va fi eliberată până când procesul respectiv nu-l eliberează sau nu se termină.

0
adăugat

Cea mai bună explicație am văzut în capitolul 7 din Fundamente de programare ebook .

Practic, în .NET, o scurgere de memorie apare atunci când obiectele menționate sunt înrădăcinate și astfel nu pot fi colectate gunoi. Acest lucru se întâmplă în mod accidental atunci când vă mențineți la referințe dincolo de scopul dorit.

Veți ști că aveți scurgeri atunci când începeți să obțineți excepții excepționale sau dacă utilizarea memoriei dvs. depășește ceea ce vă așteptați (perfmon are contoare de memorie plăcută).

Înțelegerea modelului de memorie .NET este cel mai bun mod de a evita acest lucru. În mod specific, înțelegerea modului în care funcționează colectorul de gunoi și cum funcționează referințele (din nou, vă refer la capitolul 7 al cărții electronice). De asemenea, aveți grijă de capcane comune, probabil evenimentele cele mai frecvente fiind. Dacă obiectul A înregistrat la un eveniment pe obiectul B, atunci obiectul A va rămâne în jur până când obiectul B dispare deoarece B deține referința la A. Soluția este să vă dezarhivați evenimentele când ați terminat.

Desigur, un profil de memorie bun vă va permite să vedeți graficele obiectului și să explorați cuibăritul / referința obiectelor dvs. pentru a vedea de unde provin referințele și ce obiect rădăcină este responsabil ( profilul de furnici roșii , JetBrains dotMemory, memprofiler sunt alegeri foarte bune, sau puteți utiliza doar text-windbg și sos, dar aș recomanda cu tărie un produs comercial / vizual cu excepția cazului în care sunteți un adevărat guru).

Cred că un cod nesupravegheat este supus unor scurgeri de memorie tipice de cod unamanged, cu excepția faptului că referințele partajate între cele două sunt gestionate de colectorul de gunoi. Ar putea fi în neregulă cu privire la acest ultim punct.

0
adăugat
@Jeffry acest lucru este un mod neconvențional de a descrie ce se întâmplă și îmi place!
adăugat autor Exitos
@kyoryu: Cum se face o rădăcină obiect în sine?
adăugat autor Andrei Rînea
Îți place cartea? Am văzut autorul pop-up pe stackoverflow din timp în timp.
adăugat autor Johnno Nolan
Unele obiecte .NET pot, de asemenea, să se rădăcească și să devină nerecuperabile. Orice poate fi eliminat din cauza asta.
adăugat autor kyoryu
@Andrei: Cred că un Running Thread este probabil cel mai bun exemplu al unui obiect înrădăcinat. Un obiect care pune o referință la el însuși într-o locație statică non-publică (cum ar fi abonarea la un eveniment static sau implementarea unui singleton prin inițierea câmpului static) s-ar putea să fi înrădăcinat, deoarece nu există nici o modalitate evidentă de a ... um ... "să-l răstoarne" de la ancorare.
adăugat autor Jeffrey Hantin
Codul nemanager / nativ este în mod obișnuit mai vulnerabil la scurgerile fizice, dar nu la fel de mult ca cele logice / înrădăcinate. Pentru că dacă avem una din aceste resurse înrădăcinate care nu reușesc să fie "dezrădăcinate", în mod obișnuit, ei vor ajunge în mod explicit distruși în orice caz într-o limbă precum C. Eroarea care apare în aceste cazuri este mai frecvent indicatorul și, adesea, din segfault asociate cu accesarea acestuia. Deși acest lucru este uneori de preferat să aibă această resursă înrădăcinată pur și simplu consumă memorie până când aplicația este închisă (un accident
adăugat autor Team Upvote

Toate scurgerile de memorie sunt rezolvate prin terminarea programului.

Scurtați suficientă memorie și sistemul de operare poate decide să remediați problema în numele dvs.

0
adăugat

Dacă trebuie să diagnosticați o scurgere de memorie în .NET, verificați aceste linkuri:

http: // msdn .microsoft.com / en-ne / revista / cc163833.aspx

http: // msdn .microsoft.com / en-ne / revista / cc164138.aspx

Aceste articole descriu modul de creare a unui depozit de memorie al procesului dvs. și modul de analizare a acestuia, astfel încât să puteți stabili mai întâi dacă scurgerea dvs. este neadministrată sau gestionată și, dacă este gestionată, cum să aflați de unde provine.

Microsoft are, de asemenea, un instrument mai nou pentru a asista la generarea haldelor de accidente, pentru a înlocui ADPlus, numit DebugDiag.

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

0
adăugat

Using CLR Profiler from Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en is a great way to determine which objects are holding memory, what execution flow leads to the creation of these objects, and also monitoring which objects live where on the heap (fragmentation, LOH, etc.).

0
adăugat

De asemenea, rețineți că .NET are două grămezi, una fiind mormanul obiectului mare. Cred că obiecte de aproximativ 85k sau mai mari sunt puse pe această grămadă. Această grămadă are o normă diferită de viață decât cea normală.

Dacă creați structuri de memorie mari (dicționar sau listă), ar fi prudent să mergeți la căutare care sunt regulile exacte.

În ceea ce privește recuperarea memoriei la terminarea procesului, cu excepția cazului în care rulează Win98 sau echivalente, totul este eliberat înapoi la sistemul de operare la terminare. Singurele excepții sunt lucrurile care sunt deschise în cadrul proceselor interprofesionale și un alt proces are încă o resursă deschisă.

Obiectele COM pot fi dificile. Dacă utilizați întotdeauna modelul IDispose , veți fi în siguranță. Dar am întâlnit câteva ansambluri interop care implementează IDispose . Cheia aici este de a apela Marshal.ReleaseCOMObject când ați terminat cu ea. Obiectele COM încă utilizează numărarea standard de referință COM.

0
adăugat

Am găsit Net Memory Profiler un ajutor foarte bun atunci când găsiți scurgeri de memorie în .Net. Nu este liber ca Microsoft CLR Profiler, dar este mai rapid și mai mult în punctul meu de vedere. A

0
adăugat

Cred că întrebările "ce este o scurgere de memorie" și "ce sunt efectele" au răspuns deja bine, dar am vrut să adaug încă câteva lucruri la celelalte întrebări ...

Cum să înțelegeți dacă aplicația dvs. scapă

Un mod interesant este să deschideți perfmon și să adăugați urme pentru # octeți în toate mulțimile și # Gen 2 colecții proces. Dacă exercitarea unei anumite caracteristici determină creșterea numărului total de octeți și dacă memoria rămâne alocată după următoarea colecție Gen 2, s-ar putea spune că această caracteristică scapă de memorie.

Cum să preveniți

Alte opinii bune au fost date. Aș adăuga că, probabil, cauza cel mai frecvent ignorată cauze de scurgere de memorie .NET este de a adăuga manipulatoare de evenimente la obiecte fără a le elimina. Un manipulator de evenimente atașat la un obiect este o formă de referință pentru acest obiect, astfel încât va împiedica colectarea chiar și după ce toate celelalte referințe au dispărut. Amintiți-vă întotdeauna să detașați managerii de evenimente (utilizând sintaxa - = în C #).

Scurgerea se scurge atunci când procesul se încheie și ce se întâmplă cu interopul COM?

Când procesul dvs. iese, toate memoriile cartografiate în spațiul său de adrese sunt recuperate de sistemul de operare, inclusiv orice obiecte COM deservite din DLL-uri. Comparabil rar, obiectele COM pot fi servite din procese separate. În acest caz, când procesul dvs. iese, este posibil să rămâneți responsabil pentru memoria alocată în orice proces de server COM pe care l-ați utilizat.

0
adăugat

Cea mai bună explicație a modului în care funcționează colectorul de gunoi este în Jeff Richters CLR via C # , (capitolul 20). Citirea acestui fapt oferă o bază foarte bună pentru înțelegerea modului în care obiectele persistă.

Una dintre cele mai frecvente cauze de înrădăcinare a obiectelor este întâlnirea accidentală a evenimentelor outisde o clasă. Dacă ați conectat un eveniment extern

de exemplu.

SomeExternalClass.Changed += new EventHandler(HandleIt);

și uitați să vă descurcați atunci când dispuneți, apoi SomeExternalClass are un reflex pentru clasa voastră.

După cum sa menționat mai sus, profilul de memorie SciTech este excelent pentru a vă arăta rădăcinile obiectelor despre care bănuiți că sunt scurgeri.

Dar există și o modalitate foarte rapidă de a verifica dacă un anumit tip este doar WnDBG (puteți chiar utiliza acest lucru în fereastra imediată VS.NET în timp ce atașați):

.loadby sos mscorwks
!dumpheap -stat -type 

Now do something that you think will dispose the objects of that type (de exemplu. close a window). It's handy here to have a debug button somewhere that will run System.GC.Collect() a couple of times.

Then run !dumpheap -stat -type again. If the number didn't go down, or didn't go down as much as you expect, then you have a basis for further investigation. (I got this tip from a seminar given by Ingo Rammer).

0
adăugat

De ce oamenii cred că o scurgere de memorie în .NET nu este la fel ca orice altă scurgere?

O scurgere de memorie este când atașați o resursă și nu o lăsați să meargă. Puteți face acest lucru atât în ​​modul gestionat, cât și în cel neadministrat.

În ceea ce privește .NET și alte instrumente de programare, au existat idei despre colectarea gunoiului și alte modalități de a minimiza situațiile care vor face scurgerea aplicației dumneavoastră. Dar cea mai bună metodă de a preveni scurgerile de memorie este că trebuie să înțelegeți modelul de memorie care stau la baza și modul în care funcționează lucrurile pe platforma pe care o utilizați.

Crezând că GC și alte magie vă vor curăța mizeria este calea scurtă spre scurgeri de memorie și va fi dificil de găsit mai târziu.

În cazul în care codificarea nu este gestionată, în mod normal, asigurați-vă că ați curățat, știți că resursele pe care le luați, va fi responsabilitatea dvs. de a curăța, nu de la portar.

În .NET, pe de altă parte, o mulțime de oameni cred că GC va curăța totul. Păi, o face pentru tine, dar trebuie să te asiguri că este așa. .NET nu împachetează o mulțime de lucruri, așa că nu știți întotdeauna dacă aveți de-a face cu o resursă gestionată sau ne-gestionată și trebuie să vă asigurați cu ce aveți de-a face. Manipularea fonturilor, a resurselor GDI, a directorului activ, a bazelor de date etc. este de obicei lucruri pe care trebuie să le priviți.

În termeni gestionați, îmi voi pune gâtul   linia să spună că nu mai merge o dată   procesul este ucis / eliminat.

Văd că o mulțime de oameni au acest lucru, totuși, și chiar sper că acest lucru se va termina. Nu puteți cere utilizatorului să vă desființeze aplicația pentru a vă curăța mizeria! Uitați-vă la un browser, care poate fi IE, FF etc, apoi deschideți, să zicem, Google Reader, lăsați-l să stea câteva zile și să priviți ce se întâmplă.

Dacă deschideți apoi o altă filă în browser, navigați la un anumit site, apoi închideți fila care a găzduit cealaltă pagină care a scos din browser, credeți că browserul va elibera memoria? Nu la fel cu IE. Pe calculatorul meu, IE va mânca cu ușurință 1 GB de memorie într-o perioadă scurtă de timp (aproximativ 3-4 zile) dacă folosesc Google Reader. Unele ziare sunt chiar mai rele.

0
adăugat

One definition is: Unable to release unreachable memory, which can no longer be allocated to new process during execution of allocating process. It can mostly be cured by using GC techniques or detected by automated tools.

Pentru mai multe informații, accesați http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html .

0
adăugat