Sziasztok! Tehát ha jól értem, akkor a probléma röviden annyi, hogy a saját User Controlok közül jónéhány bent marad a memóriában, vagyis nem hívódik meg a Dispose()-uk (feltehetően programozói figyelmetlenségből). Nyilván egymásba ágyazott Control-ok esetén nehéz felderíteni, hogy pontosan hol felejtődött el a Dispose hívása, vagy egy override esetén a base.Dispose() is könnyedén kimaradhat. Küldök két fájlt - ugyan buhera, de nálam az előző levelemben írt memóriaszemetelő alkalmazásomra simán működött a következő: minden saját User Controlt a UserControl osztály helyett a RegisterableUserControl osztályból származtassunk (kecses kis search&replace), aki a konstruktorában feliratkozik egy központi nyilvántartásba (ControlRegistry). Diszpozáláskor/finalize-kor pedig leiratkozik onnan, illedelmesen. Ha megfelelően meghízott az alkalmazásod memóriahasználata, akkor csattanj bele a debugger-be, és QuickWatch-csal tekintsd meg a ControlRegistry statikus osztály ControlLocator tulajdonságát, ami egy olyan listából áll, amiben csak az olyan vezérlők szerepelnek, akiket a GC nem tudott eltakarítani, mivel még nem diszpozálódtak. Egy listaelem pedig a vezérlő típusából, szülővezérlőinek listájából és a létrehozásának CallStack-jéből áll (vagyis a pontos hívási láncból, aminek végén a RegisterableControl szólt a ControlRegistrynek). A Controlokat hashcode alapján tároltuk, ezért nincs újabb referencia tárolva, nem hozzuk zavarba a GC-t. Nekem így sikerült lokalizálnom ezt a mű-szivárgást, úgyhogy szerintem egy próbát megér. (Ez a módszer nagyon közel van ahhoz, amit egyébként Ákos körvanalazott) üdv, pody -----Original Message----- From: eaf-bounce@xxxxxxxxxxxxx [mailto:eaf-bounce@xxxxxxxxxxxxx] On Behalf Of Zimler Attila Tamas Sent: Sunday, May 11, 2008 10:38 PM To: eaf@xxxxxxxxxxxxx Subject: [eaf] Re: Referencia számlálás Schmuck Ákos wrote: > > 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-2a8392a60c2 9.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... > Az eredeti problémának semmi összefüggése nincs modális ablakokkal, a TableLayoutPanel.Controls.Clear() kapcsán került elő a probléma. -- 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-----
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Diagnostics; namespace MemoryLeakTester { public static class ControlRegistry { public class ControlLocatorInfo { public string ControlTypeInfo; public string Parents; public string CallStack; public ControlLocatorInfo(string t, string p, string c) { ControlTypeInfo = t; Parents = p; CallStack = c; } } private static Dictionary<long, ControlLocatorInfo> controls = new Dictionary<long, ControlLocatorInfo>(1024); public static void Register(Control control) { if (!controls.ContainsKey(control.GetHashCode())) controls.Add(control.GetHashCode(), new ControlLocatorInfo(control.ToString(), "", new StackTrace(2).ToString())); } public static void Unregister(Control control) { if (controls.ContainsKey(control.GetHashCode())) controls.Remove(control.GetHashCode()); } public static void Update(Control control) { if (controls.ContainsKey(control.GetHashCode())) { controls[control.GetHashCode()].Parents = GetParentsTree(control); controls[control.GetHashCode()].ControlTypeInfo = control.ToString(); } } private static string GetParentsTree(Control ctrl) { StringBuilder sb = new StringBuilder(); while (ctrl != null) { if (ctrl.Parent != null) sb.AppendLine(ctrl.Parent.ToString()); ctrl = ctrl.Parent; } return sb.ToString(); } public static List<ControlLocatorInfo> ControlLocator { get { // Force finalize GC.Collect(); List<ControlLocatorInfo> list = new List<ControlLocatorInfo>(); foreach (KeyValuePair<long, ControlLocatorInfo> kv in controls) { list.Add(kv.Value); } return list; } } } }
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; namespace MemoryLeakTester { public class RegisterableUserControl : UserControl { public RegisterableUserControl() { ControlRegistry.Register(this); } protected override void Dispose(bool disposing) { ControlRegistry.Unregister(this); base.Dispose(disposing); } protected override void OnParentChanged(EventArgs e) { base.OnParentChanged(e); if (Parent != null) ControlRegistry.Update(this); } ~RegisterableUserControl() { ControlRegistry.Unregister(this); } } }