[eaf] Re: Referencia számlálás

  • From: Zimler Attila Tamas <hijaszu@xxxxxxxxxxxx>
  • To: eaf@xxxxxxxxxxxxx
  • Date: Thu, 08 May 2008 19:55:21 +0200

Podoski Peter wrote:
Helló!

Köszi a részletes leírást, így már világosabb a helyzet. Továbbra is mindenkit óva intenék ilyen jellegű problémára .NET-es referencia számláló implementációtól. A 
Control-okra bizony hívogatni kell a Dispose()-t, amennyiben jelentős mennyiségű erőforrást foglaltál le bennük. (Egyáltalán, minden IDisposable-re illik Dispose()-t mondani, amikor már nem használod, 
lásd "using" blokkok, mint burkolt try-catch-finally automatikus diszpozáló nyelvi elem).

Nyilván, de addig is meg kellene találnom, hogy hol nincs meghívva. Nem azt mondom, hogy a referencia számlálás azt oldaná meg, hogy ne kelljen meghívni. A referencia számlálás azt oldaná meg, hogy megtaláljam, mire nincs meghívva.
A Controls.Clear() nagyjából ugyanazt csinálja mint egy ArrayList.Clear() - mivel ő egy ArrayList-et használ belül (amellett, hogy az IList-et implementálja), gyakorlatilag tényleg csak egy listából tűnik el a referenciád. 
Ha csak ennyit "takarítasz", akkor ugyan már nincs referenciád a programodon belül a Control-ra, viszont egy Win32 Handle rajta van a Controlon (ami a form Handle-je is). Így a TableLayout-ba pakolt Control-ok Dispose() hívás 
nélkül a TableLayout szülő formjának megszűnéséig élni fognak, a GC nem fogja őket halottnak nyilvánítani. Ha Dispose()-t hívsz rájuk, akkor szemétté alakulnak (valószínűleg nem 
szabadulnak fel).

Az egy teljesen más kérdés, hogy miért nem csökken a program  feladatkezelőben is látható memóriahasználata. Amikor a GC takarít, akkor igazából csak összegyűjti a szemetet (megjelöli őket), igazi kidobás csak memóriafoglaláskor történik - by design. Készítettem egy 
példaprogramot, amiben egy olyan UserControl volt, aki 50 megás tömböt foglalt le születése után (és használta is). Beraktam 20 darabot a TableLayoutPanel-be, majd Dispose() hívás után kivettem őket, és ezt megismételtem jó sokszor, közben nézve és kiíratva a formra a memóriahasználatot. A 
helyzet az, hogy a GC elég okos ahhoz, hogy eldöntse, hogy mit műveljen a memóriával, nagyon nem kell aggódni miatta (a programozó az értékes idejét inkább az üzleti logika implementálása fordíthatja - ez az elsődleges "aspektus").
Nem triviális controllunkban kellően sok hiba van ahhoz, hogy ez ne történjen meg (mármint, hogy a GC ne tudja automatikusan felszabadítani a hibásat).
Nem ismeretlen számomra a C++-os memóriakezelés szabadsága, de itt egyszerűen nem kell megijedni, hogyha a 200 megányi tényleges 
memóriahasználat a feladatkezelőben 800 megának látszik, majd újabb 50 foglalásakor 500 lesz, és újabb 50 mega foglalásakor 
300, és a Dispose() után is marad 300.
Nagyszerű, mi 1,5G-nál járunk, de ez engem nem érdekelne annyira (később ki kellene amúgy is optimalizálni a kódot és folyamatban is van az újraírása). Ami zavar, hogy elfogy a window handle.
Még egy dologra regálnék:
"(Ennyit arról, hogy majd a nyelv valamit megcsinál a programozó helyett.)"
Ez nem a csak a keretrendszerre utalt, hanem arra, hogy a keretrendszer milyen szemléletet ültet a programozókba. Mivel a legtöbb helyen nem kell a memória felszabadítással foglalkozni, programozóink ott se gondoltak arra, hogy kellhet, ahol kivételesen kellett volna (és ezért nem is teljesen hibáztathatók). Ha már azt mondta a rendszer, hogy nem kell felszabadítani, ebben az esetben is megcsinálhatta volna eszerint a filozófia szerint.
Dispose()-t neked kell hívni a Control-ra, minden mást megold a keretrendszer. Ha mégis memory leaked van, akkor bizonyára a kódban 
van a hiba, és ezt addig fent is tartom, ameddig nem tudsz mutatni egy olyan mintaprogramot, ami ténylegesen leakel, pedig 
rendeltetésszerűen van diszpozálva benne mindenki. (nem kell éles projektből részlet, ki lehet emelni belőle a 
lényeget)
Nem fogok példaprogramot adni, mert ha redukálom már nem fog szivárogni, az éles kódot pedig nyilván nem kaphatja meg senki. Bár lehet, hogy majd megpróbálok demonstrációs kódot írni.
(Az aktuális memóriahasználtot a GC.GetTotalMemory(false) hívással tudod 
kideríteni - de ezt biztos ismered.)
Ezt mint mondtam még profiler eszközzel is ki tudom deríteni (és nem szivárog - a szó klasszikus értelmében -, mert pont az a baj, hogy a szemétgyűjtő nem hajlandó valamiért összeszedni), épp csak nem tudom meghatározni, mely objektumok vannak még legálisan a memóriában és melyekre nincs már referenciánk és mégis ott vannak (tehát disposolnunk kellett volna). És hogy ne legyen triviális egy naptár modulról van szó, amely többszáz napot és a megfelelő adatait tartja a memóriában (úgy kb 300 ezer objektum péládonyonként). Azt hiszem ilyen nagyságrendben azért nem működik az egyesével megvizsgálom, "te élsz még?" megközelítés.

H.

--
Az eletben vannak szabalyok
Nehanyat meg lehet kerulni, a tobbit pedig meg lehet szegni
(The Matrix)

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenKeyServer v1.2
Comment: Extracted from belgium.keyserver.net

mQGiBEE/HTYRBACInacMGc8B/lkX3CXh5D9wUDlmza4hoatNZjJmWNnPWC9c0h2V
Pi9zYJ1bVqeUnNDzCRpovPdwrswWvb9WpVOS0A7TafzWxCYud6gN8g5KiC7cSuJV
cxI0uv3jWSjUkLnECqTpm9piM4WptXrdFLxUyrKPu+Nl82QSjfdbB+8xXwCg+rWO
KeOZZGGGIYWu+rZ0M/MqyCUD/jqQVc7alF8+5y//zNDml3THTc3ljK8CLcHxFv6F
uADmvHSePRTR1ACBV3dTxL8awvanRsXC25LOuYgM1hF9RRwof59xNtXVttvo724b
FDR+oRDVKyoEm6Istfn9xbbNqRqPfk83SHvect/hlHsfgaTBKd+IRAEkbMIdRin/
E45fA/0bzklqLGj4a8qSxMMvZ1Ib+2RmH5I6o9o33FY3Nmc7IEXJizyTyQaASYCx
ldjuRmfWSOP3gQRrH5owuZq5KBCC/PNMNnUsA4noFmvIilA/9UYqMtQ0BNr+miIM
9/LV70ZbBHVpDLjb2baMjbKrmCFf9rjlc17fFsWk5ZJuE3/UxLQrWmltbGVyIEF0
dGlsYSBUYW3DoXMgPGhpamFzenVAaGxmc2xpbnV4Lmh1PohXBBMRAgAXBQJBPx02
BQsHCgMEAxUDAgMWAgECF4AACgkQ+3OFVYni0PSysACffD6VDtoer3aBe8Gpvn8e
gwiNNH8AmwcmBIrZeCOeobIuZZp3CCOgQblIuQENBEE/HTkQBACKNzv7gS0fgeiO
AMoss2bbO7X9GuO3ufc3+zx2yGV7SvYAuyWLOSMhs1ZYBVp6IawDY88zEx81oxRj
Jd4zO8h/3BI9cmjZ4NAmdv7lWjTs0I0ijFnemcFQaffxN8vIB9DqA4oyJ0A/bS7J
0E/i13sr7hRgB6NZbpy4PSSkczggvwADBQP8D3eakqeJHf1yLmw1XmQVDxHLxtrI
VL0TShFEfMGU/MLfKpcmrd43EsSondsNNm8jv4b9Y/Wgc8XPQQrfPodC5mKt8mQC
F/ovWBFs46YvhS5pojQmp6i9octTWw2OqVn3QgrBFqm4QvayLE9wRLm/xgaFApVI
4RiOOEA6SidURGCIRgQYEQIABgUCQT8dOQAKCRD7c4VVieLQ9HoZAJ4xR5Y1qlnC
LTn1GkbEW0Va+72EqwCfQTP37ylZGrvpYnFoewBIMuLFHM8=
=K0/l
-----END PGP PUBLIC KEY BLOCK-----


Other related posts: