我们首先来分析占用最多的System.String类型,看看有什么发现。
0:000> !dumpheap -mt 00007ffdb9386948 -min 200 //查看200byte以上的string Address MT Size ... 0000021bcbaf5158 00007ffdb9386948 1140 0000021d375d1038 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 Name 00007ffdb9386948 10991 76632628 System.String Total 10991 objects从上面的输出可以发现:
单个System.String类型最大占用2M以上。
超过200byte的字节的大小的System.String总大小也不过76M。(所以我们也不必深究大的String对象。)
那我们索性挑一个小点的对象来看看存储的是什么字符串,来满足一下我们的好奇心。
0.000> !do 0000021bcbaf5158 //使用!do命令查看一个对象的内容 Name: System.String MethodTable: 00007ffdb9386948 EEClass: 00007ffdb8c850e0 Size: 1140(0x474) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll String: 5b13710029d012False2052_T_BD_MATERIAL_MATERIAL.FAuxPropertyIdFBaseUnitIdFCategoryIDFChargeIDFCheckIncomingFDefaultVendorFErpClsIDFInvPtyIdFIsAffectPlanFIsAffectPlan1FIsBatchManageFIsComControlFIsEnableFIsEnable1FIsExpParToFlotFIsInventoryFIsPRFIsReturnMaterialFIsSourceControlFIsVmiBusinessFNameFNumberFPlanModeFPurchasePriceUnitIdFPurchaseUnitIdFPurPriceURNomFPurPriceURNumFPurURNomFPurURNumFReceiveAdvanceDaysFReceiveDelayDaysFReceiveMaxScaleFReceiveMinScaleFSalePriceUnitIdFSaleUnitIdFSpecificationFStockIdFStockPlaceIdFStoreUnitIDFTaxTypeFUseOrgId111193 Fields: MT Field Offset Type VT Attr Value Name 00007ffdb9389288 400026f 8 System.Int32 1 instance 557 m_stringLength 00007ffdb9387b00 4000270 c System.Char 1 instance 35 m_firstChar 00007ffdb9386948 4000274 90 System.String 0 shared static Empty >> Domain:Value 00000218c6c4d220:NotInit 0000021d52d81840:NotInit <<似乎是基础资料字段信息。那接下来使用!gcroot命令查看其对应的GC根,看看到底是什么对象持有其引用,导致占用内存得不到释放。
0:000> !gcroot 0000021bcbaf5158 //使用!gcroot 查看一个对象的gc根 HandleTable: 00000218c6ff15e8 (pinned handle) -> 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.String Found 1 unique roots (run '!GCRoot -all' to see all roots).从以上输出可以看出:
该String类型被一个Hashset所持有。
从Cache关键字可以看出该String类型是被缓存所持有。
分析到这里,我们大致可以得出一个结论:
String类型占用4G内存,绝大多数是由缓存所占用,才导致String类型得不到释放。
那我们是不是可以猜测内存占用持续走高是不是被缓存撑爆的呢?。
带着这个疑问我们来继续分析下Kingdee.BOS.JSON.JSONArray类型。
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命令中止输出。