Sziasztok! Ha kicsit késve is, de bekapcsolódnék a beszélgetésbe... Szóval tesztelgettem a problémát, meg közben Google-özgettem egy kicsit, és úgy a második-harmadik kulccsszó kombinációra (c# dispose winforms) első helyen kiadott egy lapot amivel a gond megoldható (javítható), bár nem autómatikusan, de azért talán kezelhető mértékben. A link: http://www.reflectionit.nl/Blog/PermaLink7a2db2fc-eff3-4ad0-9e8c-2a8392a60c29.aspx A lényeg: Modálisan megjelenített ablakoknak a Dispose fv-e nem hívódik meg autómatikusan! Csináltam egy kis példaprogit (előre is bocs, egy-két elég csúnya dolgot csinálok benne :) ami szépen demonstrálja a dolgot. Mivel ha jól rémlik TabControl / TabPage-ekről volt szó, én is egy TabControl-t tettem fel, egy gombbal lehet bele pakolgatni TabPage-eket, minden TabPage-en egyetlen származtatott label van, ami nem csinál mást, mint konstruktorra kiírja, hogy ő létrejött, Dispose-ra meg kiírja hogy halálán van :) Alul van három gomb, szépen meg lehet figyelni, hogy a sima Show, ill. a "módosított" ShowModal szépen műxik, de a sima ShowModal-ra egy Dispose sem hívódik meg (direkt kiraktam egy gombra a GC.Collect() -et, de persze ez nem a gond forrása, nem is segít) A "módosítás" egyébként csak annyi, hogy egy 'using' blokkba raktam a form megjelenítését. Tehát ha minden helyen, ahol modálisan jelenítetek meg ablakot, átírjátok using-ra (vagy a cikkben mutatott try/finally-s megoldásra) akkor minden olyan leak-et bezártatok, ami a control-okhoz kötődik (saját osztályok persze maradnak, de ha azokat pl. a formon deklaráltátok, akkor a form Dispose-át lehet override-olni, és be lehet oda tenni a saját osztályok Dispose-át) Nem tudom ez mennyi munka, de talán kevesebb, mint leak-eket keresgélni... Remélem segített ;) Ákos PS.: Ha mindenképpen autómatizálni kéne, akkor vmi olyasmit csinálnék, hogy lenne egy singleton, és egy származtatott form. A form létrehozásakor felregisztrálja magát (akár call-stack-el együtt) a singleton-nál, bezáráskor leregisztrálja magát. A Singleton a program végén minden le-nem regisztrált formot jelentene. Ez után már csak a kódban előforduló 'Form' típusokat kéne lecseréni az új típusra. Persze itt a dolog neheze, de hát egy felügyelt Replace csak nem tart olyan sokáig (vagy legalábbis rövidebb ideig mint tovább bogarászni a kódot, hogy hol van a baj...?! :) Minden más autómatizálással kapcsolatos ötletem ugyanez, csak futásidőben Reflection + kódgenerálással, rövid távon ennél azért egy sima Replace egyszerűbb lesz :D PS2.: a progi VS2008-as, de .NET 2.0 WinForms-os projekt. Link: http://people.inf.elte.hu/kossch/MemTest.zip PS3.: .NET-es leak-keresgéléshez két cikk (ha mégis kéne :) : http://www.codeproject.com/KB/dotnet/Memory_Leak_Detection.aspx http://www.codeproject.com/KB/debug/WinDBGAndSOS.aspx 2008/5/8 Zimler Attila Tamas <hijaszu@xxxxxxxxxxxx>: > 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----- > > >