WinDbg 分析高内存占用问题

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学习笔记。
接下来就是一通命令乱敲,我尽量解释清晰。
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类型,看看有什么发现。
0:000> !dumpheap -mt 00007ffdb9386948 -min 200 //查看200byte以上的stringAddress MT Size...0000021bcbaf5158 00007ffdb9386948 11400000021d375d1038 00007ffdb9386948 1496980000021d375f5920 00007ffdb9386948 1496980000021d3765b138 00007ffdb9386948 1497060000021d37f739c8 00007ffdb9386948 2171200000021d37fa8a08 00007ffdb9386948 1901620000021d38047330 00007ffdb9386948 12246980000021d3829d348 00007ffdb9386948 12246980000021d386bd678 00007ffdb9386948 26109940000021d38bb8500 00007ffdb9386948 2610994Statistics:MT Count TotalSize Class Name00007ffdb9386948 10991 76632628 System.StringTotal 10991 objects
从上面的输出可以发现:
单个
System.String类型最大占用 2M 以上。超过 200byte 的字节的大小的
System.String总大小也不过 76M。(所以我们也不必深究大的 String 对象。)
那我们索性挑一个小点的对象来看看存储的是什么字符串,来满足一下我们的好奇心。
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 <<
似乎是基础资料字段信息。那接下来使用!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.StringFound 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 4000000218c933f060 00007ffd5c24a068 4000000218c7605990 00007ffd5c24a068 4000000218c7605af0 00007ffd5c24a068 4000000218c7605c50 00007ffd5c24a068 4000000218c7605e18 00007ffd5c24a068 4000000218c7605fa0 00007ffd5c24a068 4000000218c7606198 00007ffd5c24a068 4000000218c7606338 00007ffd5c24a068 4000000218c76064b0 00007ffd5c24a068 40User interrupt.
从输出结果来看:
满屏都是 40byte 的 JSONArray。只能使用
Ctrl+Break命令中止输出。
但为了保险期间,我们来验证下有没有 100byte 以上的JSONArray。
0:000> !dumpheap -mt 00007ffd5c24a068 -min 100Address MT SizeStatistics:MT Count TotalSize Class NameTotal 0 objects
这时我们可以大胆猜测所有的JSONArray对象都是 40byte。从而可以得出另一个猜测占用 560M 内存的 JSONArray,都具有相似的对象结构。接下来我们来验证这个猜测。随机选择几个对象,看看其内容具体是什么。
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 命令来解开数组的面纱。
0:000> !DumpArray /d 0000021975972b48Name: 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:7MT 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 <<
从以上输出可以看出,其共有 8 个子项,我们再随机挑几个子项看看是什么内容。
0:000> !DumpObj /d 0000021975972a70Name: System.StringMethodTable: 00007ffdb9386948EEClass: 00007ffdb8c850e0Size: 42(0x2a) bytesFile: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllString: FHTZDLBFields:MT Field Offset Type VT Attr Value Name00007ffdb9389288 400026f 8 System.Int32 1 instance 8 m_stringLength00007ffdb9387b00 4000270 c System.Char 1 instance 50 m_firstChar00007ffdb9386948 4000274 90 System.String 0 shared static Empty>> Domain:Value 00000218c6c4d220:NotInit 0000021d52d81840:NotInit <<0:000> !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 <<
我们可以看到一个字符串内容是FHTZDLB,另一个是发货通知单列表。看到这,我立马就条件反射的想到,这不就是我们的菜单信息嘛。为了验证我的想法,连续查看几个JSONArray,都是相似的内容。
这时,我们继续发扬敢猜敢做的精神。是不是内存被菜单缓存撑爆的?!
为了验证这一猜测,我们继续从 Dump 中寻找佐证。使用~* e!clrstack来看看所有线程的调用堆栈吧。
0:000> ~* e!clrstackOS Thread Id: 0x11dc (0)Unable to walk the managed stack. The current thread is likely not amanaged thread. You can run !threads to get a list of managed threads inthe processFailed to start stack walk: 80070057OS Thread Id: 0x2b2c (28)Child SP IP Call Site00000076cff7ecc8 00007ffdca2e6bf4 [HelperMethodFrame_1OBJ: 00000076cff7ecc8] System.Threading.WaitHandle.WaitMultiple(System.Threading.WaitHandle[], Int32, Boolean, Boolean)00000076cff7ee00 00007ffdb91af67c System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\waithandle.cs @ 454]00000076cff7ee60 00007ffdb201b2fb System.Net.TimerThread.ThreadProc()00000076cff7ef10 00007ffdb915ca72 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]00000076cff7efe0 00007ffdb915c904 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]00000076cff7f010 00007ffdb915c8c2 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 891]00000076cff7f060 00007ffdb9196472 System.Threading.ThreadHelper.ThreadStart() [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 111]00000076cff7f2b8 00007ffdbb4f6793 [GCFrame: 00000076cff7f2b8]00000076cff7f608 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076cff7f608]00000076cff7f798 00007ffdbb4f6793 [ContextTransitionFrame: 00000076cff7f798]00000076cff7f9c8 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076cff7f9c8]OS Thread Id: 0x1bd4 (133)Child SP IP Call SiteGetFrameContext failed: 10000000000000000 0000000000000000OS Thread Id: 0x1a98 (134)Child SP IP Call Site00000076dbdbcc88 00007ffdca2e6124 [InlinedCallFrame: 00000076dbdbcc88] .SNIReadSyncOverAsync(SNI_ConnWrapper*, SNI_Packet**, Int32)00000076dbdbcc88 00007ffdaaaf5dd4 [InlinedCallFrame: 00000076dbdbcc88] .SNIReadSyncOverAsync(SNI_ConnWrapper*, SNI_Packet**, Int32)00000076dbdbcc60 00007ffdaaaf5dd4 DomainNeutralILStubClass.IL_STUB_PInvoke(SNI_ConnWrapper*, SNI_Packet**, Int32)00000076dbdbcd10 00007ffdaab08fe3 SNINativeMethodWrapper.SNIReadSyncOverAsync(System.Runtime.InteropServices.SafeHandle, IntPtr ByRef, Int32)00000076dbdbcd70 00007ffdaabe0ae0 System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()00000076dbdbcdd0 00007ffdaabe09dd System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()00000076dbdbce10 00007ffdaabdf7f5 System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()00000076dbdbce50 00007ffdaabdfa0e System.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte ByRef)00000076dbdbce90 00007ffdaabc7daa System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject, Boolean ByRef)00000076dbdbcff0 00007ffdaabbb3c7 System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()00000076dbdbd050 00007ffdaabb8325 System.Data.SqlClient.SqlDataReader.get_MetaData()00000076dbdbd0a0 00007ffdaab3be73 System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.RunBehavior, System.String, Boolean, Boolean)00000076dbdbd110 00007ffdaab3b75f System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, Boolean, Int32, System.Threading.Tasks.Task ByRef, Boolean, Boolean, System.Data.SqlClient.SqlDataReader, Boolean)00000076dbdbd1f0 00007ffdaab3a763 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String, System.Threading.Tasks.TaskCompletionSource`1, Int32, System.Threading.Tasks.Task ByRef, Boolean ByRef, Boolean, Boolean)00000076dbdbd2c0 00007ffdaab3a49b System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String)00000076dbdbd360 00007ffdaab35cc6 System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior, System.String)00000076dbdbd3f0 00007ffd5c517ead Kingdee.BOS.App.Data.AbstractDatabase.DoExecuteReader(System.Data.Common.DbCommand, System.Data.CommandBehavior)00000076dbdbd450 00007ffd5c515ebb Kingdee.BOS.App.Data.AbstractDatabase.ExecuteReader(System.Data.Common.DbCommand, System.Collections.Generic.IEnumerable`1, System.Data.CommandBehavior, Boolean)00000076dbdbd4d0 00007ffd5c4fd6f2 Kingdee.BOS.App.Data.AbstractDatabase.ExecuteReader(System.Data.Common.DbCommand, System.Collections.Generic.IEnumerable`1, System.Data.CommandBehavior)00000076dbdbd500 00007ffd5c4e31b1 Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String, System.Collections.Generic.IEnumerable`1, System.Data.CommandType, System.Data.CommandBehavior, Boolean)00000076dbdbd570 00007ffd5c51d2a7 Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String, System.Collections.Generic.IEnumerable`1, System.Data.CommandType, Boolean)00000076dbdbd5b0 00007ffd5c51d2fc Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String, System.Data.CommandType, Boolean)00000076dbdbd5e0 00007ffd5c51d341 Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String)00000076dbdbd610 00007ffd5ca5a5d4 Kingdee.BOS.App.Core.MainConsole.MainConsoleServer.GetSearchMenuData(Kingdee.BOS.Context, System.String, System.Collections.Generic.HashSet`1 ByRef, System.Collections.Generic.HashSet`1 ByRef)00000076dbdbd7d0 00007ffd5ca58164 Kingdee.BOS.App.Core.MainConsole.MainConsoleServer.GetMenuArrayForCache(Kingdee.BOS.Context)00000076dbdbda78 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076dbdbda78]00000076dbdbddb8 00007ffdbb4f6793 [HelperMethodFrame_PROTECTOBJ: 00000076dbdbddb8] System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)00000076dbdbdf30 00007ffdb914b690 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[]) [f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs @ 761]00000076dbdbdfa0 00007ffdb9142922 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo) [f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs @ 735]00000076dbdbe020 00007ffdb9143f22 System.Reflection.MethodBase.Invoke(System.Object, System.Object[]) [f:\dd\ndp\clr\src\BCL\system\reflection\methodbase.cs @ 211]00000076dbdbe060 00007ffd5c61990c Microsoft.Practices.Unity.InterceptionExtension.InterceptingRealProxy+c__DisplayClass1.b__0(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextInterceptionBehaviorDelegate)00000076dbdbe160 00007ffd5c619477 Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior+c__DisplayClass1.b__0(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbdbe200 00007ffd5c61cbc4 Kingdee.BOS.Cache.KCacheMethodCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbdbe350 00007ffd5c61b10b Kingdee.BOS.Performance.Publisher.PerformanceCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbdbe3e0 00007ffd5c61b10b Kingdee.BOS.Performance.Publisher.PerformanceCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbdbe470 00007ffd5c61b10b Kingdee.BOS.Performance.Publisher.PerformanceCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbdbe500 00007ffd5c61936d Microsoft.Practices.Unity.InterceptionExtension.HandlerPipeline.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.InvokeHandlerDelegate)00000076dbdbe590 00007ffd5c618999 Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextInterceptionBehaviorDelegate)00000076dbdbe620 00007ffd5c61845d Microsoft.Practices.Unity.InterceptionExtension.InterceptionBehaviorPipeline.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.InvokeInterceptionBehaviorDelegate)00000076dbdbe6b0 00007ffd5c617002 Microsoft.Practices.Unity.InterceptionExtension.InterceptingRealProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage)00000076dbdbe790 00007ffdb911190c System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(System.Runtime.Remoting.Proxies.MessageData ByRef, Int32) [f:\dd\ndp\clr\src\BCL\system\runtime\remoting\realproxy.cs @ 823]00000076dbdbe980 00007ffdbb4f4a02 [TPMethodFrame: 00000076dbdbe980] Kingdee.BOS.Contracts.IMainConsoleServer.GetMenuArrayForCache(Kingdee.BOS.Context)00000076dbdbec00 00007ffdb91ad436 System.Threading.Tasks.Task.Execute() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2498]00000076dbdbec40 00007ffdb915ca72 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]00000076dbdbed10 00007ffdb915c904 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]00000076dbdbed40 00007ffdb91ad6dc System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2827]00000076dbdbedf0 00007ffdb91acdf3 System.Threading.Tasks.Task.ExecuteEntry(Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2767]00000076dbdbee30 00007ffdb9194882 System.Threading.ThreadPoolWorkQueue.Dispatch() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 820]00000076dbdbf2c8 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076dbdbf2c8]00000076dbdbf458 00007ffdbb4f6793 [ContextTransitionFrame: 00000076dbdbf458]00000076dbdbf688 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076dbdbf688]OS Thread Id: 0x153c (135)Child SP IP Call SiteGetFrameContext failed: 10000000000000000 0000000000000000OS Thread Id: 0x242c (136)Child SP IP Call SiteGetFrameContext failed: 10000000000000000 0000000000000000OS Thread Id: 0x153c (135)Child SP IP Call SiteGetFrameContext failed: 10000000000000000 0000000000000000OS Thread Id: 0x242c (136)Child SP IP Call SiteGetFrameContext failed: 10000000000000000 0000000000000000OS Thread Id: 0x2a04 (137)Child SP IP Call Site00000076dbf7af08 00007ffdca2e6124 [InlinedCallFrame: 00000076dbf7af08] .SNIReadSyncOverAsync(SNI_ConnWrapper*, SNI_Packet**, Int32)00000076dbf7af08 00007ffdaaaf5dd4 [InlinedCallFrame: 00000076dbf7af08] .SNIReadSyncOverAsync(SNI_ConnWrapper*, SNI_Packet**, Int32)00000076dbf7aee0 00007ffdaaaf5dd4 DomainNeutralILStubClass.IL_STUB_PInvoke(SNI_ConnWrapper*, SNI_Packet**, Int32)00000076dbf7af90 00007ffdaab08fe3 SNINativeMethodWrapper.SNIReadSyncOverAsync(System.Runtime.InteropServices.SafeHandle, IntPtr ByRef, Int32)00000076dbf7aff0 00007ffdaabe0ae0 System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()00000076dbf7b050 00007ffdaabe09dd System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()00000076dbf7b090 00007ffdaabdf7f5 System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()00000076dbf7b0d0 00007ffdaabdfa0e System.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte ByRef)00000076dbf7b110 00007ffdaabc7daa System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject, Boolean ByRef)00000076dbf7b270 00007ffdaabbb3c7 System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()00000076dbf7b2d0 00007ffdaabb8325 System.Data.SqlClient.SqlDataReader.get_MetaData()00000076dbf7b320 00007ffdaab3be73 System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.RunBehavior, System.String, Boolean, Boolean)00000076dbf7b390 00007ffdaab3b75f System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, Boolean, Int32, System.Threading.Tasks.Task ByRef, Boolean, Boolean, System.Data.SqlClient.SqlDataReader, Boolean)00000076dbf7b470 00007ffdaab3a763 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String, System.Threading.Tasks.TaskCompletionSource`1, Int32, System.Threading.Tasks.Task ByRef, Boolean ByRef, Boolean, Boolean)00000076dbf7b540 00007ffdaab3a49b System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String)00000076dbf7b5e0 00007ffdaab35cc6 System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior, System.String)00000076dbf7b670 00007ffd5c517ead Kingdee.BOS.App.Data.AbstractDatabase.DoExecuteReader(System.Data.Common.DbCommand, System.Data.CommandBehavior)00000076dbf7b6d0 00007ffd5c515ebb Kingdee.BOS.App.Data.AbstractDatabase.ExecuteReader(System.Data.Common.DbCommand, System.Collections.Generic.IEnumerable`1, System.Data.CommandBehavior, Boolean)00000076dbf7b750 00007ffd5c4fd6f2 Kingdee.BOS.App.Data.AbstractDatabase.ExecuteReader(System.Data.Common.DbCommand, System.Collections.Generic.IEnumerable`1, System.Data.CommandBehavior)00000076dbf7b780 00007ffd5c4e31b1 Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String, System.Collections.Generic.IEnumerable`1, System.Data.CommandType, System.Data.CommandBehavior, Boolean)00000076dbf7b7f0 00007ffd5c51d2a7 Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String, System.Collections.Generic.IEnumerable`1, System.Data.CommandType, Boolean)00000076dbf7b830 00007ffd5c61737a Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Kingdee.BOS.Context, System.String, System.Collections.Generic.List`1)00000076dbf7b860 00007ffd5c8d2bd7 Kingdee.BOS.App.Core.UserParameterService.GetParamter(Kingdee.BOS.Context, Int64, System.String, System.String)00000076dbf7bb68 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076dbf7bb68]00000076dbf7bea8 00007ffdbb4f6793 [HelperMethodFrame_PROTECTOBJ: 00000076dbf7bea8] System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)00000076dbf7c020 00007ffdb914b690 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[]) [f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs @ 761]00000076dbf7c090 00007ffdb9142922 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo) [f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs @ 735]00000076dbf7c110 00007ffdb9143f22 System.Reflection.MethodBase.Invoke(System.Object, System.Object[]) [f:\dd\ndp\clr\src\BCL\system\reflection\methodbase.cs @ 211]00000076dbf7c150 00007ffd5c61990c Microsoft.Practices.Unity.InterceptionExtension.InterceptingRealProxy+c__DisplayClass1.b__0(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextInterceptionBehaviorDelegate)00000076dbf7c250 00007ffd5c619477 Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior+c__DisplayClass1.b__0(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbf7c2f0 00007ffd5c61cbc4 Kingdee.BOS.Cache.KCacheMethodCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbf7c440 00007ffd5c61b10b Kingdee.BOS.Performance.Publisher.PerformanceCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbf7c4d0 00007ffd5c61b10b Kingdee.BOS.Performance.Publisher.PerformanceCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbf7c560 00007ffd5c61b10b Kingdee.BOS.Performance.Publisher.PerformanceCallHandler.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextHandlerDelegate)00000076dbf7c5f0 00007ffd5c61936d Microsoft.Practices.Unity.InterceptionExtension.HandlerPipeline.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.InvokeHandlerDelegate)00000076dbf7c680 00007ffd5c618999 Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.GetNextInterceptionBehaviorDelegate)00000076dbf7c710 00007ffd5c61845d Microsoft.Practices.Unity.InterceptionExtension.InterceptionBehaviorPipeline.Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation, Microsoft.Practices.Unity.InterceptionExtension.InvokeInterceptionBehaviorDelegate)00000076dbf7c7a0 00007ffd5c617002 Microsoft.Practices.Unity.InterceptionExtension.InterceptingRealProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage)00000076dbf7c880 00007ffdb911190c System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(System.Runtime.Remoting.Proxies.MessageData ByRef, Int32) [f:\dd\ndp\clr\src\BCL\system\runtime\remoting\realproxy.cs @ 823]00000076dbf7ca70 00007ffdbb4f4a02 [TPMethodFrame: 00000076dbf7ca70] Kingdee.BOS.Contracts.IUserParameterService.GetParamter(Kingdee.BOS.Context, Int64, System.String, System.String)00000076dbf7ccf0 00007ffd5c8d28c5 Kingdee.BOS.App.Security.K3CloudLoginService.SetRegionInfo(Kingdee.BOS.Context, Kingdee.BOS.Contracts.IUserParameterService, Kingdee.BOS.Orm.DataEntity.DynamicObject)00000076dbf7cd70 00007ffd5c22b2e2 Kingdee.BOS.App.Security.K3DataCenterService.GetDataCenterContextByID(System.String)00000076dbf7cdc0 00007ffd5c227d97 Kingdee.BOS.App.Security.K3CloudLoginService+c__DisplayClass8.b__3(System.String)00000076dbf7cdf0 00007ffd5c228471 Kingdee.BOS.Core.Authentication.AbstractAuthService.LoadContext(Kingdee.BOS.Core.Authentication.LoadContextArg)00000076dbf7ce50 00007ffd5c226ed8 Kingdee.BOS.App.Security.K3CloudLoginService.Login(Kingdee.BOS.Performance.Common.PerformanceContext, Kingdee.BOS.Authentication.LoginInfo)00000076dbf7ced0 00007ffd5c20b5a9 Kingdee.BOS.ServiceHelper.LoginServiceHelper.Login(Kingdee.BOS.Performance.Common.PerformanceContext, System.String, Kingdee.BOS.Authentication.LoginInfo)00000076dbf7cf30 00007ffd5c20960f Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateLoginInfo(System.String, Kingdee.BOS.Authentication.LoginInfo)00000076dbf7d080 00007ffd5c20783c Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser(System.String, System.String, System.String, Int32)00000076dbf7d318 00007ffdbb4f6793 [DebuggerU2MCatchHandlerFrame: 00000076dbf7d318]00000076dbf7d658 00007ffdbb4f6793 [HelperMethodFrame_PROTECTOBJ: 00000076dbf7d658] System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)00000076dbf7d7d0 00007ffdb914b690 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[]) [f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs @ 761]00000076dbf7d840 00007ffdb9142922 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo) [f:\dd\ndp\clr\src\BCL\system\reflection\methodinfo.cs @ 735]00000076dbf7d8c0 00007ffd5c206201 Kingdee.BOS.ServiceFacade.KDServiceFx.ServiceExecutor.Execute(Kingdee.BOS.ServiceFacade.KDServiceFx.KDServiceContext, Kingdee.BOS.ServiceFacade.KDServiceFx.ServiceType, System.String[], Kingdee.BOS.ServiceFacade.SerializerProxy, Kingdee.BOS.ServiceFacade.KDServiceFx.ServiceType)00000076dbf7d960 00007ffd5c203ea9 Kingdee.BOS.ServiceFacade.KDServiceFx.ExecuteServiceModule.OnProcess(Kingdee.BOS.ServiceFacade.KDServiceFx.KDServiceContext)00000076dbf7da00 00007ffd5c203ab8 Kingdee.BOS.ServiceFacade.KDServiceFx.ModulePipeline.ExcuteRequest(Kingdee.BOS.ServiceFacade.KDServiceFx.KDServiceContext)00000076dbf7da50 00007ffd5c20123e Kingdee.BOS.ServiceFacade.KDServiceFx.RequestExcuteRuntime.StartRequest(Kingdee.BOS.ServiceFacade.KDServiceFx.RequestExtractor, Kingdee.BOS.ServiceFacade.KDServiceFx.WebContext)00000076dbf7daa0 00007ffd5c200f00 Kingdee.BOS.ServiceFacade.KDServiceFx.KDSVCHandler.ExecuteRequest(Kingdee.BOS.ServiceFacade.KDServiceFx.WebContext, Kingdee.BOS.ServiceFacade.KDServiceFx.RequestExtractor)00000076dbf7dae0 00007ffd5c200d45 Kingdee.BOS.ServiceFacade.KDServiceFx.KDSVCHandler.ProcessRequestInternal(Kingdee.BOS.ServiceFacade.KDServiceFx.WebContext, Kingdee.BOS.ServiceFacade.KDServiceFx.RequestExtractor)00000076dbf7db30 00007ffdac1a373e *** WARNING: Unable to verify checksum for System.Web.ni.dllSystem.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()00000076dbf7dbc0 00007ffdac1633fb System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)00000076dbf7dc10 00007ffdac178220 System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Exception)00000076dbf7dd70 00007ffdac163f79 System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext, System.AsyncCallback)00000076dbf7ddc0 00007ffdac1766c3 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest, System.Web.HttpContext)00000076dbf7de40 00007ffdac165398 System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)00000076dbf7e000 00007ffdac164f63 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)00000076dbf7e040 00007ffdac8646ba DomainNeutralILStubClass.IL_STUB_ReversePInvoke(Int64, Int64, Int64, Int32)00000076dbf7e8b0 00007ffdbb4f21fe [InlinedCallFrame: 00000076dbf7e8b0] System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr, System.Web.RequestNotificationStatus ByRef)00000076dbf7e8b0 00007ffdac1d2dde [InlinedCallFrame: 00000076dbf7e8b0] System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr, System.Web.RequestNotificationStatus ByRef)00000076dbf7e880 00007ffdac1d2dde DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, System.Web.RequestNotificationStatus ByRef)00000076dbf7e940 00007ffdac16556f System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)00000076dbf7eb00 00007ffdac164f63 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)00000076dbf7eb40 00007ffdac8646ba DomainNeutralILStubClass.IL_STUB_ReversePInvoke(Int64, Int64, Int64, Int32)00000076dbf7ed18 00007ffdbb4f2453 [ContextTransitionFrame: 00000076dbf7ed18]
通过仔细比对发现这么一条Kingdee.BOS.App.Core.MainConsole.MainConsoleServer.GetMenuArrayForCache(Kingdee.BOS.Context)调用堆栈。从方法命名来看,像是用来获取菜单数组并缓存。结合前后堆栈的联系,我们可以大致得出这样一个线索:用户使用 WebApi 登录后会缓存一份独立的菜单供用户使用。
有了代码堆栈,接下来知道怎么干了吧?当然是核实源代码确定问题啊。
4. 分析源码验证问题
Kingdee.BOS.App.Core.MainConsole.MainConsoleServer.GetMenuArrayForCache(Kingdee.BOS.Context)方法源代码如下:
我们发现它是用的UserToken来缓存用户菜单。看到Token,你可能就会条件反射的想到其生命周期。是的,聪明贤惠如你,Token 是有生命周期的。也就意味着 Token 过期后,下次登录还会再次缓存一份菜单。你可能会问 Token 过期后没有去清对应的菜单缓存吗?是的,并没有。
严谨的你,可能又会问 Token 多久过期?20mins。你眼珠子一转,接着问,满打满算,一个用户 1 个小时也就申请 3 次 Token,24 小时,也就申请 72 个 Token,一个菜单缓存也就顶多 1K,所以一个用户一天也就最多占用 72K。你的网站得有多少并发,才能被这么多菜单缓存撑爆啊?!
Good Question!!!
是的,客户的应用场景的并发也就顶多几百而已。那到底是什么导致如此多的菜单缓存呢?
原因是,客户的第三方客户端使用 WebApi 与我们的系统对接。而每次调用 WebApi 时都会先去调用登录接口,但却未保存会话信息。也就是说,客户第三方客户端每次的 WebApi 调用都会产生一个新的 Token。那如果有成千上万的 WebApi 请求,也就意味着成千上万的菜单缓存啊。
好了,点到为止。至此,已经基本定位到问题的根源了。
5. 最后
也许很多同学没有接触过 WinDbg,觉得其是一个复杂的工具。其实通过本文的案例讲解,其无非是通过一系列常见的命令来进行问题跟踪来定位问题。
最后来简单总结下,Windbg 分析问题的步骤:
创建完整 Dump 文件
Windbg 加载 Dump 文件
根据不同问题类型,使用相关的命令进行分析
耐心分析,抽丝剥茧
边分析边猜测边验证
结合源码验证猜想
修复验证
版权声明: 本文为 InfoQ 作者【圣杰】的原创文章。
原文链接:【http://xie.infoq.cn/article/060d33cc5ff9e8b3e13e7ed1a】。文章转载请联系作者。
圣杰
一名立志成为架构师并为之努力奋斗的程序员 2018.03.14 加入
.NET Developer, Microsoft MVP











评论