玉扇

注册

 

发新话题 回复该主题

记一次NET某桌面奇侠游戏非托管 [复制链接]

1#
一:背景1.讲故事

说实话,这篇dump我本来是不准备上一篇文章来解读的,但它有两点深深的感动了我。

无数次的听说用Unity可做游戏开发,但百闻不如一见。

游戏中有很多金庸武侠小说才有的名字,太赏心悦目了。

dfa玉骨扇dfcd云龙枪dfd8阴风爪df7a雪魂丝链dfad乙木神剑df04003星耀冠df3159532乌金锤...

所以说这么好的一个dump,我得给它留下点什么。

好了,话说回来这个缘分起于上个月有位朋友说它的程序虚拟内存占用非常大,咨询如何解决,如下图:

先甭管是什么问题,多抓几个dump总不会错的,几经折腾后发了一个dump过来。

二:Windbg分析1.到底是哪里的泄漏

分析内存方面的问题,还是那句话,一分为二看一下到底是哪一块的内存泄漏(托管还是非托管)。

先看一下进程总内存,使用!address-summary命令。

0address-summary---UsageSummary----------------RgnCount-----------TotalSize--------%ofBusy%ofTotalFreeffe`9e6a(.TB).00%Heap`fd(4.GB)72.51%0.00%unknown`2c6ad(.MB)12.56%0.00%Stack`2a(.MB)11.88%0.00%Image4`0a970(.MB)3.00%0.00%Other`dc(1.MB)0.03%0.00%TEB`50(1.MB)0.02%0.00%PEB10`00(4.kB)0.00%0.00%---TypeSummary(forbusy)------RgnCount-----------TotalSize--------%ofBusy%ofTotalMEM_PRIVATE`f(5.GB)95.36%0.00%MEM_IMAGE`0aa6b(.MB)3.01%0.00%MEM_MAPPED`05bce(91.MB)1.62%0.00%---StateSummary----------------RgnCount-----------TotalSize--------%ofBusy%ofTotalMEM_FREEffe`9e6a(.TB).00%MEM_COMMIT`1c740(4.GB)80.45%0.00%MEM_RESERVE`45207(1.GB)19.55%0.00%

从卦中得知MEM_COMMIT=4.4G,接下来再看下托管堆的内存占用,可以用命令!eeheap-gc命令。

0eeheap-gcNumberofGCHeaps:1generation0startsat0xdfdc48generation1startsat0xdfbgeneration2startsat0xdf30fc0ephemeralsegmentallocationcontext:nonesegmentbeginallocatedsizedf30fc0df30fc0dfcae00x7cbae0()Largeobjectheapstartsat0xdf40fc0segmentbeginallocatedsizedf40fc0df40fc0dfb80xa27b8()TotalSize:Size:0x86e()bytes.------------------------------GCHeapSize:Size:0x86e()bytes.

从卦中得知GCHeapSize=Byte=8M,我去,才这么点,有点开玩笑哈!!!????????,很明显这是非托管内存泄漏,既然方向已定,那就排查下非托管区域吧!

2.探究非托管泄漏

按照经验,寻找非托管泄漏,首先看下loader堆,很多程序往往是因为动态创建了太多程序集所致,比如经典的Castle,XmlSerializer,有兴趣的朋友可以网上找下这方面的资料,这里使用!eeheap-loader命令查看。

0eeheap-loader--------------------------------------JitcodeheapoaderCodeHeap:0(0:0)Size:0x0(0)bytes.Totalsize:Size:0x0(0)bytes.--------------------------------------ModuleThunkheaps:Module07ffda5fa0:Size:0x0(0)bytes.Module07ffdc:Size:0x0(0)bytes.Module07ffda2630:Size:0x0(0)bytes.Module07ffda5330:Size:0x0(0)bytes.Module07ffdac620:Size:0x0(0)bytes.Module07ffdac4e0:Size:0x0(0)bytes.Module07ffda48b0:Size:0x0(0)bytes.Module07ffda1790:Size:0x0(0)bytes.Module07ffdb:Size:0x0(0)bytes.Totalsize:Size:0x0(0)bytes.--------------------------------------ModuleLookupTableheaps:Module07ffda5fa0:Size:0x0(0)bytes.Module07ffdc:Size:0x0(0)bytes.Module07ffda2630:Size:0x0(0)bytes.Module07ffda5330:Size:0x0(0)bytes.Module07ffdac620:Size:0x0(0)bytes.Module07ffdac4e0:Size:0x0(0)bytes.Module07ffda48b0:Size:0x0(0)bytes.Module07ffda1790:Size:0x0(0)bytes.Module07ffdb:Size:0x0(0)bytes.Totalsize:Size:0x0(0)bytes.--------------------------------------TotalLoaderHeapsize:Size:0x99()bytestotal,0x2()byteswasted.=======================================

从输出看otalLoaderHeapsize=K,看样子这次踏空了,那就进困难模式看看WindowsNT堆,这里使用!heap-s命令。

0heap-s************************************************************************************************************************NTHEAPSTATSBELOW************************************************************************************************************************LFHKey:0xb6c37b3e3a4aeTerminationoncorruption:ENABLEDHeapFlagsReservCommitVirtFreeListUCRVirtLockFast(k)(k)(k)(k)lengthblockscont.heap-------------------------------------------------------------------------------------df2e60024145083LFHdf2e1f446421df2e8300218601721155200LFHdf2ec80021860236107200LFHdfe0086021df30bb0426086051df49bd002840446033LFHdf49b204218609618LFHdf30b400206df30b300218601521LFHdf4bbb002390412924LFHdf899200218603721147200LFHdf89be186028012LFHdf56f4323722620436bLFHdf56f61860176121LFHdf89ac390421604eLFH-------------------------------------------------------------------------------------

从输出信息看:原来程序的内存都被heap=df2e60给吸走了,那就深挖它吧,这里用!heap-stat-hdf2e60命令看一下该heap的统计信息。

0ext.heap-stat-hdf2e60heap

df2e60group-byOTSIZEmax-display:20size#blockstotal(%)(percentoftotalbusybytes)20cfd2-99fa4(68.76)d-(24.17)12ce8-2d1c3e0(1.26)21d1c46-19f0b26(0.72)-18dc(0.69)ad00-1842(0.68)a1d3ebb-e(0.51)10f8d99-f8d(0.43)adae-(0.24)bb-7b(0.22)-793(0.21)b-ff(0.21)c-d4(0.18)9afef9-62f6c1(0.17)d6a80f-c3(0.15)f4f5a9-4a64e7(0.13)e-49f(0.13)88b-(0.12)b-3a(0.10)d06-31a17e(0.09)

从输出信息看,这块heap主要是被size=2和size=58给填满了,毕竟他们占比68.76+24.17=92.93,所以挖他们很有必要,接下来用命令!heap-flts2找出heap中所有的这些block的首地址。

0ext.heap-flts2_HEAP

df2e60HEAP_ENTRYSizePrevFlagsUserPtrUserSize-statedf2edd002[00]df2ede002-(busy)df2e72c7e[00]df2e72c7f002-(busy)df400c[00]df400d002-(busy)df420d[00]df420e002-(busy)df440e[00]df440f002-(busy)df460f[00]df4602-(busy)df48[00]df4812-(busy)df4a1201[00]df4a12002-(busy)df4c12[00]df4c13002-(busy)df4e13[00]df4e14002-(busy)df5014[00]df5015002-(busy)...

上面的HEAP_ENTRY就是block的首地址,由于这样的block大概有4cfd2=31.5w个,没法一一列出,接下来就是用dc去观察这些block的内存块内容来发现其中规律,手工肯定太麻烦了,还是得借助下脚本,这里还是取前1w条查看。

functionshow_all_blocksize(){varoutput=exec("!ext.heap-flts58").Take(00);for(varlineofoutput){varheap_entry_address=line.trim().split(")[0];if(heap_entry_address.indexOf("00")==-1)continue;show_heap_entry(heap_entry_address);}}functionshow_heap_entry(heap_entry_address){varpageIndex=(index++);varpath=".writememD:\\file\\"+pageIndex+".txt"+heap_entry_address+"L?0x58";varoutput=exec(path);log("pageIndex="+pageIndex);}

执行脚本生成到txt之后,截图如下:

通过观察发现,这个heap中有大量的用户信息,然后就拿这些信息求证朋友了。

和朋友简单沟通后,我也只能帮到这里,到此结案。

三:总结

本次事故的原因是由于C#调用Lua后,Lua未作合理的内存释放造成的非托管泄漏,具体怎么在代码层进行释放,这个要看朋友的造化了。

最后上一个小彩蛋,朋友太客气了。

没见过这么大的红包,我居然收了??????,反手就给公司研发小伙伴一人一杯下午茶,在这里对朋友说一声感谢??????

END

工作中的你,是否已遇到...

1.CPU爆高

2.内存暴涨

3.资源泄漏

4.崩溃死锁

5.程序呆滞

等紧急事件,全公司都指望着你能解决...危难时刻才能展现你的技术价值,作为专注于.NET高级调试的技术博主,欢迎

分享 转发
TOP
发新话题 回复该主题