{"id":961,"date":"2009-11-30T10:55:49","date_gmt":"2009-11-30T09:55:49","guid":{"rendered":"http:\/\/soci.hu\/blog\/?p=961"},"modified":"2009-11-30T10:55:49","modified_gmt":"2009-11-30T09:55:49","slug":"managed-memory-leak-nyomozas-windbg-vel","status":"publish","type":"post","link":"https:\/\/soci.hu\/blog\/index.php\/2009\/11\/30\/managed-memory-leak-nyomozas-windbg-vel\/","title":{"rendered":"Managed memory leak nyomoz\u00e1s WinDBG-vel"},"content":{"rendered":"<p>Im\u00e1dom a WinDbg-t, mondtam m\u00e1r? Az egyik kis programom m\u00f3dszeresen eszegette a mem\u00f3ri\u00e1t. Ciklusban v\u00e9gez feldolgoz\u00e1st, rettent\u0151 sok adattal, \u00e9s ezek egy r\u00e9sze sz\u00e9pen bennragadt a mem\u00f3ri\u00e1ban. .NET-ben nincs memory leak a klasszikus \u00e9rtelemben, de van oly m\u00f3don, hogy a rootokb\u00f3l marad referencia egyes objektumokra, \u00edgy azok \u00e9lve maradnak, sz\u00e1nd\u00e9kaink ellen\u00e9re. N\u00e9ha azonban nem trivi\u00e1lis kinyomozni, melyik root miatt ragadt be valami a mem\u00f3ri\u00e1ba.<br \/>\nA VSTS profiler seg\u00edts\u00e9g\u00e9vel od\u00e1ig el lehet jutni, hogy ki ragad be a mem\u00f3ri\u00e1ba. Az Object LifeTime n\u00e9zetben az Instances alive at end oszlopra rendeztetve l\u00e1thatjuk, kik maradnak \u00e9lve a program v\u00e9g\u00e9n. \u00c9n beraktam egy force-olt GC-z\u00e9st a program v\u00e9g\u00e9re, \u00edgy aki ezek ut\u00e1n m\u00e9g benn maradt, azok egy r\u00e9sze sz\u00e1nd\u00e9kaim ellen\u00e9re tette ezt, ez\u00e9rt azt leaknek tekintem, \u00e9s meg kell sz\u00fcntetni.<br \/>\nJ\u00f6het a WinDbg. File, Open Executable, F5. A program v\u00e9g\u00e9n a GC ut\u00e1n raktam m\u00e9g egy Console.ReadLine()-t. Amikor ide eljut, a debuggerben CTRL-Breakkel meg\u00e1ll\u00edtom a program fut\u00e1s\u00e1t (v\u00e1rakoz\u00e1s\u00e1t). Bet\u00f6lt\u00f6m az <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb190764.aspx\">sos<\/a>-t:<br \/>\n.load C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.21006\\sos.dll<\/p>\n<p>Kilist\u00e1ztatom a GC heapen lev\u0151 ojjektumokat:<br \/>\n!DumpHeap -stat<\/p>\n<p>000007ff005c2278     2766       221280 System.Data.Metadata.Edm.TypeUsage<br \/>\n000007fef1b90d00      370       222136 System.Byte[]<br \/>\n000007fef1b89be0    13379      1330896 System.String<br \/>\n000007fef1b8c8a8     1125      1447552 System.Int32[]<br \/>\n000007fef1b3bef0     9298      3224392 System.Object[]<br \/>\n000007ff00274988   245954     29514480 ATS.Bar<\/p>\n<p>Akinek nem szabadna m\u00e1r a mem\u00f3ri\u00e1ban lenni, az az ATS.Bar objektumok, 245954 darab, 29514480 b\u00e1jt m\u00e9retben. N\u00e9zz\u00fck megy a p\u00e9ld\u00e1nyokat bel\u0151le:<\/p>\n<p>!DumpHeap -type ATS.Bar<\/p>\n<p>0000000004952c60 000007ff00274988      120<br \/>\n0000000004952cd8 000007ff00274988      120<br \/>\n0000000004952d50 000007ff00274988      120<br \/>\n0000000004952dc8 000007ff00274988      120<br \/>\n0000000004952e40 000007ff00274988      120<br \/>\n0000000004952eb8 000007ff00274988      120<br \/>\ntotal 0 objects<br \/>\nStatistics:<br \/>\n              MT    Count    TotalSize Class Name<br \/>\n000007ff00fb38a0        1           24 System.Collections.Generic.GenericEqualityComparer`1[[ATS.BarCollectionDescriptor, Common]]<br \/>\n000007ff00271050        1           24 ATS.BarDAL<br \/>\n000007ff00270e68        1           48 ATS.BarFactory<br \/>\n000007ff00fb31b8        1           88 System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]]<br \/>\n000007ff00fb27a8        1           88 System.Collections.Generic.Dictionary`2[[ATS.BarCollectionDescriptor, Common],[ATS.BarFromTickFactory, Common]]<br \/>\n000007ff00275378        1           88 System.Collections.Generic.Dictionary`2[[ATS.Symbol, Common],[ATS.BarCollectionByInterval, Common]]<br \/>\n000007ff00fb3ee0        1           96 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]][]<br \/>\n000007ff00fb3b10        1           96 System.Collections.Generic.Dictionary`2+Entry[[ATS.BarCollectionDescriptor, Common],[ATS.BarFromTickFactory, Common]][]<br \/>\n000007ff00fb0498        1           96 System.Collections.Generic.Dictionary`2+Entry[[ATS.Symbol, Common],[ATS.BarCollectionByInterval, Common]][]<br \/>\n000007ff00fb2d30        3          120 System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]]<br \/>\n000007ff00790990        3          120 ATS.BarCollectionDescriptor<br \/>\n000007ff00fb1568        7          168 System.Collections.ObjectModel.ObservableCollection`1+SimpleMonitor[[ATS.Bar, Common]]<br \/>\n000007ff00886530        3          240 ATS.BarFromTickFactory<br \/>\n000007ff00fb1648        7          280 System.Collections.Generic.List`1[[ATS.Bar, Common]]<br \/>\n000007ff00273788        7          952 ATS.BarCollection<br \/>\n000007ff00274988   245954     29514480 ATS.Bar<br \/>\nTotal 245993 objects<\/p>\n<p>A kis 120 b\u00e1jtos iz\u00e9k a probl\u00e9m\u00e1sak (a v\u00e9g\u00e9n az \u00f6sszefoglal\u00f3 az\u00e9rt tartalmaz t\u00f6bb t\u00edpust is, mert substring sz\u0171r\u00e9st csin\u00e1l a -type). Az els\u0151 oszlop az egyedi objektumok c\u00edme, a m\u00e1sodik a t\u00edpus met\u00f3dusle\u00edr\u00f3 t\u00e1bl\u00e1ja.<br \/>\n\u00c9s most j\u00f6n a l\u00e9nyeg. Ki miatt \u00e9rhet\u0151 el a rootokb\u00f3l mondjuk az utols\u00f3 Bar p\u00e9ld\u00e1ny?<\/p>\n<p>!GCRoot 0000000004952eb8<\/p>\n<p>DOMAIN(00000000003CF530):HANDLE(Pinned):1217d8:Root:  0000000012657048(System.Object[])-><br \/>\n  0000000002bf7f28(System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]])-><br \/>\n  0000000002bf94b8(System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]][])-><br \/>\n  0000000002f06ca0(System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]])-><br \/>\n  0000000002f06cc8(System.Object[])-><br \/>\n  0000000002f05860(ATS.BarFromTickFactory)-><br \/>\n  0000000004952eb8(ATS.Bar)<\/p>\n<p>Ebben az l\u00e1tszik, az els\u0151 bejegyz\u00e9sb\u0151l, amit m\u00e9g a CLR startup hozzott l\u00e9tre hogyan lehet eljutni a beragadt objektumunkhoz. A CLR generikus neveket C#-ra visszaford\u00edtva az l\u00e1tszik, hogy egy Dictionary<int, List<BarFromTickFactory>> hivatkozik egy Dictionary.Entry<int, List<BarFromTickFactory>>-re, az a dictionary bels\u0151 t\u00e1rol\u00f3eleme. Ez r\u00e1mutat egy List<BarFromTickFactory>-re, ami tov\u00e1bbmutat egy ATS.BarFromTickFactory-ra, ami hivatkozik a Bar-ra.<\/p>\n<p>Ez alapj\u00e1n m\u00e1r rekonstru\u00e1lhat\u00f3 a probl\u00e9ma forr\u00e1sa. A probl\u00e9ma elemi oka, hogy t\u00falz\u00e1sba vittem a statikusok haszn\u00e1lat\u00e1t, \u00e9s nem figyeltem a takar\u00edt\u00e1sukra.<\/p>\n<p>static readonly Dictionary<BarCollectionDescriptor, BarFromTickFactory> FactoriesByDesc =<br \/>\n    new Dictionary<BarCollectionDescriptor, BarFromTickFactory>();<\/p>\n<p>Ebb\u0151l rendesen kiszedtem a t\u00e1rolt BarFromTickFactory p\u00e9ld\u00e1nyt, ha m\u00e1r nem volt r\u00e1 sz\u00fcks\u00e9g. De elfeledkeztem r\u00f3la, hogy volt egy m\u00e1sik kollekci\u00f3 is:<\/p>\n<p>public static readonly Dictionary<int, List<BarFromTickFactory>> FactoryListBySymbolId =<br \/>\n    new Dictionary<int, List<BarFromTickFactory>>();<\/p>\n<p>Ebb\u0151l viszont nem szedtem ki a hivatkoz\u00e1s az adott BarFromTickFactory-ra, \u00edgy az sz\u00e9pen beragadt a mem\u00f3ri\u00e1ba.<\/p>\n<p>Tanuls\u00e1gok:<br \/>\n1. Szeretj\u00fck a WinDbt-t.<br \/>\n2. Ker\u00fclj\u00fck a statikusokat. Ha a BarFromTickFactory p\u00e9ld\u00e1nyokat egy m\u00e1sok oszt\u00e1ly p\u00e9ld\u00e1nyai t\u00e1roln\u00e1k, akkor ha azok kifutnak a szk\u00f3pb\u00f3l, automatikusan a GC martal\u00e9kai lesznek. A sok statikus sok odafigyel\u00e9st ig\u00e9nyel, k\u00e1r er\u0151ltetni \u0151ket.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im\u00e1dom a WinDbg-t, mondtam m\u00e1r? Az egyik kis programom m\u00f3dszeresen eszegette a mem\u00f3ri\u00e1t. Ciklusban v\u00e9gez feldolgoz\u00e1st, rettent\u0151 sok adattal, \u00e9s ezek egy&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,10,4],"tags":[],"class_list":["post-961","post","type-post","status-publish","format-standard","hentry","category-net","category-c","category-szakmai-elet"],"_links":{"self":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/961","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=961"}],"version-history":[{"count":4,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/961\/revisions"}],"predecessor-version":[{"id":969,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/961\/revisions\/969"}],"wp:attachment":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=961"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=961"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=961"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}