查看: 186|回复: 0

Windbg分析高内存占用问题

[复制链接]

该用户从未签到

发表于 2019-11-4 16:58:30 | 显示全部楼层 |阅读模式

1. 题目简介

近来产品发布大版本补丁更新,一商超客户升级后,反馈系统经常奔溃,导致超市的收银系统无法正常收银,现场排队付款的顾客更是诉苦声声。为了缓解现场的情况, 客户都是手动回收IIS应用步伐池才能解决。

如许的后果是很严重的,接到反馈,第一时间想到的是加内存吧,如许最快。但是客户从8G-->16G-->32G,只是延长了每次奔溃的时间,但是并没有解决系统卡顿的题目。到这里,也根本猜测了题目所在了,肯定是什么东西一直在吃内存且得不到释放。这种题目,也就只能打Dump分析了。
2. 打Dump

长途客户应用服务器,32G内存占用已经消耗了78%,而现场已经反馈收银系统接近奔溃了,要求先逼迫回收内存。反正也要奔溃了,先打Dump再说吧。
(PS:打Dump会挂起进程,导致应用无法相应!而打Dump的耗时,也是根据其时进程的内存占用有关,内存占用越大,耗时越久。)
打开使命管理器,选择对应的IIS进程,右键创建转储文件(Dump)。

结果,Dump文件是生成的,结果当分析的时间,发现Windbg提示Dump无效。分析Dump文件创建的有题目。观察使命管理器,发现内存占用一下就降下来了,原来是之前的进程直接奔溃了,重启了一个W3WP进程。
既然直接从使命管理器无法创建,就使用第三方工具收集Dump吧。颠末Goggle,找到一款很好用的Dump收集工具ProcDump,是一个下令行应用,其主要用途是监视应用步伐的CPU或内存峰值并在峰值期间生成Dump。
因为是高内存占用题目,我们使用以下下令来抓取dump:
(PS:可以使用进程名称,也可以使用进程ID来指定要创建Dump的进程。当有多个相同名称的进程时,必须使用进程ID来指定!)
procdump w3wp -m 20480 -o D:\Dumps (当内存超过20G时抓取一个w3wp进程的MiniDump)
上面就是我踩得第一个坑,因为默认抓取的是MiniDump,很快就抓下来,文件也很小,正在我得意的时间,Windbg加载Dump分析的时间,发现包含的信息很少,根本无法进行进一步的分析。
调解创建Dump的下令,添加-ma参数即可创建完整Dump。
procdump w3wp -ma -m 20480 -o D:\Dumps (当内存超过20G时抓取一个w3wp进程的完整Dump)
结果再一次,当内存占用到达20G,占比80%的时间,Dump再次创建失败,提示:Procdump Error writing dump file。再一次感觉到绝望。不过至少有错误提示,google一把,果然存在天涯沦落人。Procdump Error writing dump file: 0x80070005 Error 0x80070005 (-2147024891): Access is denied。大致的意思是说,当90S内Dump文件没有成功创建的话(也就不测这w3wp进程被挂起了90s),IIS检测到w3wp进程挂起超过90s没有相应就会终止进程,重现创建一个新的进程。好嘛,真是处处是坑。
这个坑,也让我开始真正停下来思考题目。罗马不是一日建成的,内存也不是一下撑爆的。我干嘛死脑筋非要到内存占用超过80%才去打Dump呢呢呢???!
焕然大悟,如醍醐灌顶。
procdump w3wp -ma -m 8000 -o D:\Dumps (当内存超过8000M时抓取一个w3wp进程的完整Dump,并输出到D:\Dumps文件夹
此时内存占用在40%左右,这次Dump终于成功创建了。
3..分析Dump

分析Dump,上WinDbg。如果对WinDbg不理解,可以看我这篇WinDbg学习笔记
接下来就是一通下令乱敲,我只管解释清楚。
  1. 0:000> !dumpheap -stat //检查当前所有托管范例的统计信息....00007ffdb9387a98   777101     69462436 System.Char[]00007ffdb938c988   588917    115563505 System.Byte[]00007ffdb9389220  1026406    119828936 System.Int32[]00007ffdb93516a8   663559    128819040 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Object, mscorlib]][]00000218c6c30a80  6436865    197832116      Free00007ffdae9cc240    23171    273333144 System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]][]00007ffdb9391f28 13885170    333244080 System.Boolean00007ffd5c24a068 14003455    560138200 Kingdee.BOS.JSON.JSONArray00007ffdb9386fc0 14373648   1393615400 System.Object[]00007ffdb9386948 76146065   4000287202 System.StringTotal 138435970 objects
复制代码
使用dumpheap -stat下令查看当前所有托管范例的统计信息。从输出的结果来看:

  • 此中占用内存最多当属System.String范例,接近4G的巨细(是不是很吃惊?!)。
  • 其次System.Object[]范例占有1.3G巨细。
  • Kingdee.BOS.JSON.JSONArray范例也大概占用了560M。
我们起首来分析占用最多的System.String范例,看看有什么发现。
  1. 0:000> !dumpheap -mt 00007ffdb9386948 -min 200 //查看200byte以上的stringAddress               MT     Size...0000021bcbaf5158 00007ffdb9386948     11400000021d375d1038 00007ffdb9386948   149698     0000021d375f5920 00007ffdb9386948   149698     0000021d3765b138 00007ffdb9386948   149706     0000021d37f739c8 00007ffdb9386948   217120     0000021d37fa8a08 00007ffdb9386948   190162     0000021d38047330 00007ffdb9386948  1224698     0000021d3829d348 00007ffdb9386948  1224698     0000021d386bd678 00007ffdb9386948  2610994     0000021d38bb8500 00007ffdb9386948  2610994     Statistics:              MT    Count    TotalSize Class Name00007ffdb9386948    10991     76632628 System.StringTotal 10991 objects
复制代码
从上面的输出可以发现:

  • 单个System.String范例最大占用2M以上。
  • 超过200byte的字节的巨细的System.String总巨细也不过76M。(以是我们也不必深究大的String对象。)
那我们索性挑一个小点的对象来看看存储的是什么字符串,来满足一下我们的好奇心。
  1. 0.000> !do 0000021bcbaf5158 //使用!do下令查看一个对象的内容Name:        System.StringMethodTable: 00007ffdb9386948EEClass:     00007ffdb8c850e0Size:        1140(0x474) bytesFile:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllString:      5b13710029d012false2052_T_BD_MATERIAL_MATERIAL.FAuxPropertyIdFBaseUnitIdFCategoryIDFChargeIDFCheckIncomingFDefaultVendorFerpClsIDFInvPtyIdFIsAffectPlanFIsAffectPlan1FIsBatchManageFIsComControlFIsEnableFIsEnable1FIsExpParToFlotFIsInventoryFIsPRFIsReturnMaterialFIsSourceControlFIsVmiBusinessFNameFNumberFPlanModeFPurchasePriceUnitIdFPurchaseUnitIdFPurPriceURNomFPurPriceURNumFPurURNomFPurURNumFReceiveAdvanceDaysFReceiveDelayDaysFReceiveMaxScaleFReceiveMinScaleFSalePriceUnitIdFSaleUnitIdFSpecificationFStockIdFStockPlaceIdFStoreUnitIDFTaxTypeFUseOrgId111193Fields:              MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb9389288  400026f        8         System.Int32  1 instance              557 m_stringLength00007ffdb9387b00  4000270        c          System.Char  1 instance               35 m_firstChar00007ffdb9386948  4000274       90        System.String  0   shared           static Empty                                 >> Domain:Value  00000218c6c4d220:NotInit  0000021d52d81840:NotInit   0000021cc75ebe68 System.Object[]    -> 0000021bc7629a10 Kingdee.BOS.Cache.KCacheManagerFactory    -> 0000021bc7629ab8 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[Kingdee.BOS.Cache.AbstractKCacheManager, Kingdee.BOS]]    -> 0000021c4da6fa48 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[Kingdee.BOS.Cache.AbstractKCacheManager, Kingdee.BOS]][]    -> 00000218c83861b8 Kingdee.BOS.Cache.KCacheManager    -> 00000218c8386630 Kingdee.BOS.Cache.ECache.ECacheManager    -> 00000218c83866e8 System.Collections.Concurrent.ConcurrentDictionary`2[[System.String, mscorlib],[System.Collections.Generic.HashSet`1[[System.String, mscorlib]], System.Core]]    -> 0000021bcbae0c70 System.Collections.Concurrent.ConcurrentDictionary`2+Tables[[System.String, mscorlib],[System.Collections.Generic.HashSet`1[[System.String, mscorlib]], System.Core]]    -> 0000021bcbad0128 System.Collections.Concurrent.ConcurrentDictionary`2+Node[[System.String, mscorlib],[System.Collections.Generic.HashSet`1[[System.String, mscorlib]], System.Core]][]    -> 0000021bcbb34bf8 System.Collections.Concurrent.ConcurrentDictionary`2+Node[[System.String, mscorlib],[System.Collections.Generic.HashSet`1[[System.String, mscorlib]], System.Core]]    -> 0000021bcbada790 System.Collections.Concurrent.ConcurrentDictionary`2+Node[[System.String, mscorlib],[System.Collections.Generic.HashSet`1[[System.String, mscorlib]], System.Core]]    -> 0000021a49766460 System.Collections.Generic.HashSet`1[[System.String, mscorlib]]    -> 00000219540976b0 System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]][]    -> 0000021bcbaf5158 System.StringFound 1 unique roots (run '!GCRoot -all' to see all roots).
复制代码
从以上输出可以看出:

  • 该String范例被一个Hashset所持有。
  • 从Cache关键字可以看出该String范例是被缓存所持有。
分析到这里,我们大致可以得出一个结论:
String范例占用4G内存,绝大多数是由缓存所占用,才导致String范例得不到释放。
那我们是不是可以猜测内存占用持续走高是不是被缓存撑爆的呢?
带着这个疑问我们来继续分析下Kingdee.BOS.JSON.JSONArray范例。
  1. 0:000> !dumpheap -mt 00007ffd5c24a068 //输出托管堆上的所有JSONArray对象         Address               MT     Size....0000021975972b48 00007ffd5c24a068       40     00000218c933f060 00007ffd5c24a068       40     00000218c7605990 00007ffd5c24a068       40     00000218c7605af0 00007ffd5c24a068       40     00000218c7605c50 00007ffd5c24a068       40     00000218c7605e18 00007ffd5c24a068       40     00000218c7605fa0 00007ffd5c24a068       40     00000218c7606198 00007ffd5c24a068       40     00000218c7606338 00007ffd5c24a068       40     00000218c76064b0 00007ffd5c24a068       40     User interrupt.
复制代码
从输出结果来看:

  • 满屏都是40byte的JSONArray。只能使用Ctrl+Break下令中断输出。
但为了保险期间,我们来验证下有没有100byte以上的JSONArray。
  1. 0:000> !dumpheap -mt 00007ffd5c24a068 -min 100         Address               MT     SizeStatistics:              MT    Count    TotalSize Class NameTotal 0 objects
复制代码
这时我们可以大胆猜测所有的JSONArray对象都是40byte。从而可以得出另一个猜测占用560M内存的JSONArray,都具有相似的对象布局。接下来我们来验证这个猜测。随机选择几个对象,看看其内容具体是什么。
  1. 0:000> !DumpObj /d 0000021975972b48  //查看第一个JSONArrayName:        System.Object[]MethodTable: 00007ffdb9386fc0EEClass:     00007ffdb8d4aa00Size:        88(0x58) bytesArray:       Rank 1, Number of elements 8, Type CLASS (Print Array)Fields:None
复制代码
从输出可以看出:

  • JSONArray实质是System.Object[]范例。
  • 对应的MethodTable: 00007ffdb9386fc0。
如果你记性好的话,我们应当还记得占用内存第二多的就是这个System.Object[]范例,占用1.3G。翻到上面,你可以发现其MethodTable和上面的统计信息是一致的。
(PS:到这里我们是不是可以猜测:System.Object[]占用的内存无法释放,就是由于被JSONArray持有引用导致的呢?
既然是数组,就使用!DumpArray 下令来解开数组的面纱。
[code]0:000> !DumpArray /d 0000021975972b48 Name:        System.Object[]MethodTable: 00007ffdb9386fc0EEClass:     00007ffdb8d4aa00Size:        88(0x58) bytesArray:       Rank 1, Number of elements 8, Type CLASSElement Methodtable: 00007ffdb9386f28[0] 0000021975972a08[1] 0000021975972a70[2] 0000021975972a40[3] 0000021ac75e87b8[4] 0000021975972b10[5] 0000021975972ba0[6] null[7] null0:000> !DumpObj /d 0000021975972a08Name:        System.StringMethodTable: 00007ffdb9386948EEClass:     00007ffdb8c850e0Size:        54(0x36) bytesFile:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllString:      555d8ca25a6261Fields:7              MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb9389288  400026f        8         System.Int32  1 instance               14 m_stringLength00007ffdb9387b00  4000270        c          System.Char  1 instance               35 m_firstChar00007ffdb9386948  4000274       90        System.String  0   shared           static Empty                                 >> Domain:Value  00000218c6c4d220:NotInit  0000021d52d81840:NotInit  > Domain:Value  00000218c6c4d220:NotInit  0000021d52d81840:NotInit   !DumpObj /d 0000021975972a40Name:        System.StringMethodTable: 00007ffdb9386948EEClass:     00007ffdb8c850e0Size:        42(0x2a) bytesFile:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllString:      发货通知单列表Fields:              MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb9389288  400026f        8         System.Int32  1 instance                8 m_stringLength00007ffdb9387b00  4000270        c          System.Char  1 instance             6279 m_firstChar00007ffdb9386948  4000274       90        System.String  0   shared           static Empty                                 >> Domain:Value  00000218c6c4d220:NotInit  0000021d52d81840:NotInit

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?用户注册

x

相关技术服务需求,请联系管理员和客服QQ:2753533861或QQ:619920289
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

帖子推荐:
客服咨询

QQ:2753533861

服务时间 9:00-22:00

快速回复 返回顶部 返回列表