一、示例代碼html
static void Main(string[] args) { Console.Clear(); Console.WriteLine("到命令行下,切換到windbg目錄,執行adplus -hang -pn highcpu.exe -o c:\\dumps"); Console.WriteLine("若是要中止,按Ctrl+C結束程序"); Console.WriteLine("===================================================="); while (true) { Console.SetCursorPosition(0, 3); Console.Write(DateTime.Now.Ticks.ToString()); } Console.ReadKey(); }
二、運行控制檯程序後,抓取3次Dump,最好每次間隔幾分鐘。程序員
三、分別對每一個Dump執行以下指令。數據庫
第一個Dump: 0:000> .load clr20\sos.dll 0:000> !runaway User Mode Time Thread Time 0:334 0 days 0:00:00.468 3:dc8 0 days 0:00:00.000 2:14d0 0 days 0:00:00.000 1:750 0 days 0:00:00.000 第二個Dump: 0:000> .load clr20\sos.dll 0:000> !runaway User Mode Time Thread Time 0:334 0 days 0:00:08.221 3:dc8 0 days 0:00:00.000 2:14d0 0 days 0:00:00.000 1:750 0 days 0:00:00.000 第三個Dump: 0:000> .load clr20\sos.dll 0:000> !runaway User Mode Time Thread Time 0:334 0 days 0:00:11.559 3:dc8 0 days 0:00:00.000 2:14d0 0 days 0:00:00.000 1:750 0 days 0:00:00.000
從上面的輸出能夠看到,線程0的CPU時間不斷增長,CPU高佔用極可能是因爲線程0正在執行的代碼有問題形成的。服務器
能夠用!clrstack命令查看一下當前的調用堆棧以及其上的局部變量和值。app
當執行!clrstack時,報出了以下提示:async
0:000> !clrstack Failed to find runtime DLL (mscorwks.dll), 0x80004005 Extension commands need mscorwks.dll in order to have something to do.
這不是我想要的,加載mscorwks失敗。後來通過調試發現,原來是第一步就.load時就錯了,由於個人那段代碼用的是vs2010。因此應該加載.net frameword 4.0。函數
因此,最初應該執行的.load C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/sos.dll性能
而後在執行!clrstack學習
第三個Dump測試
0:000> !clrstack *** ERROR: Symbol file could not be found. Defaulted to export symbols for clr.dll - PDB symbol for clr.dll not loaded OS Thread Id: 0x334 (0) Child SP IP Call Site 001cf2b8 779e70f4 [InlinedCallFrame: 001cf2b8] 001cf2b4 6a14672e *** WARNING: Unable to verify checksum for mscorlib.ni.dll DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, COORD) 001cf2b8 6a1b6cfd [InlinedCallFrame: 001cf2b8] Microsoft.Win32.Win32Native.SetConsoleCursorPosition(IntPtr, COORD) 001cf2ec 6a1b6cfd System.Console.SetCursorPosition(Int32, Int32) 001cf324 005500bb *** WARNING: Unable to verify checksum for ConsoleApplication3.exe ConsoleApplication3.Program.Main(System.String[]) [C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 22] 001cf4dc 70a33e22 [GCFrame: 001cf4dc]
第二個Dump
0:000> !clrstack *** ERROR: Symbol file could not be found. Defaulted to export symbols for clr.dll - PDB symbol for clr.dll not loaded OS Thread Id: 0x334 (0) Child SP IP Call Site 001cf2b8 779e70f4 [InlinedCallFrame: 001cf2b8] 001cf2b4 6a14672e *** WARNING: Unable to verify checksum for mscorlib.ni.dll DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, COORD) 001cf2b8 6a1b6cfd [InlinedCallFrame: 001cf2b8] Microsoft.Win32.Win32Native.SetConsoleCursorPosition(IntPtr, COORD) 001cf2ec 6a1b6cfd System.Console.SetCursorPosition(Int32, Int32) 001cf324 005500bb *** WARNING: Unable to verify checksum for ConsoleApplication3.exe ConsoleApplication3.Program.Main(System.String[]) [C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 22] 001cf4dc 70a33e22 [GCFrame: 001cf4dc]
第一個Dump:
0:000> !clrstack *** ERROR: Symbol file could not be found. Defaulted to export symbols for clr.dll - PDB symbol for clr.dll not loaded OS Thread Id: 0x334 (0) Child SP IP Call Site 001cf2b8 779e70f4 [InlinedCallFrame: 001cf2b8] 001cf2b4 6a14672e *** WARNING: Unable to verify checksum for mscorlib.ni.dll DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, COORD) 001cf2b8 6a1b6cfd [InlinedCallFrame: 001cf2b8] Microsoft.Win32.Win32Native.SetConsoleCursorPosition(IntPtr, COORD) 001cf2ec 6a1b6cfd System.Console.SetCursorPosition(Int32, Int32) 001cf324 005500bb *** WARNING: Unable to verify checksum for ConsoleApplication3.exe ConsoleApplication3.Program.Main(System.String[]) [C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 22] 001cf4dc 70a33e22 [GCFrame: 001cf4dc]
不知道爲何,個人第三個Dump並無出現HighCPU的提示,也許是個人電腦配置有點高吧。不過,出問題的代碼已能夠看獲得,就在Main方法裏。只須要細細查看Main方法找到問題所在就OK了。
學習自:http://www.cnblogs.com/juqiang/archive/2008/01/11/1035689.html
公司的系統最近每隔一段時間就會忽然變卡,CPU暴增,24核的CPU常常跑到25%,系統卡得不能動。項目經理終於忍不住了,叫我排查一下。
首先,抓了3個DUMP,都是在系統卡的時候,隔個幾分鐘抓一次。
因爲服務器是64位的系統,而個人Win7是32位的系統,感受總是有問題,一方面因爲本身仍是一個新手,不少問題一碰到就茫然,乾脆直接偷偷在服務器裝了個Windbg。
首先,裝載如sos.dll
.load C:\Windows\Microsoft.NET\Framework64\v2.0.50727\sos.dll
而後逐個執行"!runaway"而後看下線程的CPU時間:
第一個DUMP:
47:4690 0 days 0:10:08.887
83:6914 0 days 0:08:52.727
46:6c88 0 days 0:04:13.813
45:a4d0 0 days 0:04:01.037
40:6d94 0 days 0:03:51.146
44:1c60 0 days 0:03:46.341
39:3fdc 0 days 0:03:46.107
37:eb0 0 days 0:03:28.791
41:11c0 0 days 0:03:27.184
38:9858 0 days 0:03:20.898
49:7b90 0 days 0:03:09.759
第二個DUMP:
47:4690 0 days 0:11:39.570
83:6914 0 days 0:08:54.334
46:6c88 0 days 0:04:46.339
45:a4d0 0 days 0:04:32.518
40:6d94 0 days 0:04:24.889
44:1c60 0 days 0:04:21.894
39:3fdc 0 days 0:04:21.863
37:eb0 0 days 0:04:00.335
41:11c0 0 days 0:03:57.495
38:9858 0 days 0:03:52.753
49:7b90 0 days 0:03:39.353
第三個DUMP:
47:4690 0 days 0:13:01.050
83:6914 0 days 0:08:55.645
46:6c88 0 days 0:05:16.089
45:a4d0 0 days 0:04:59.334
40:6d94 0 days 0:04:55.668
39:3fdc 0 days 0:04:53.328
44:1c60 0 days 0:04:53.079
37:eb0 0 days 0:04:31.800
41:11c0 0 days 0:04:26.496
38:9858 0 days 0:04:20.568
49:7b90 0 days 0:04:06.169
很明顯47號線程佔用CPU時間很是厲害。
查看一下特殊線程:
0:000> !threads -special
ThreadCount: 32
UnstartedThread: 0
BackgroundThread: 32
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive Lock
ID OSID ThreadOBJ State GC GC Alloc Context Domain Count APT Exception
...
83 18 6914 0000000009e7e190 180b220 Enabled 0000000000000000:0000000000000000 000000000372e6e0 1 MTA (Threadpool Worker)
...
OSID Special thread type
47 4690 GC
...
83 6914 ThreadpoolWorker
...
從輸出能夠看到,47號線程是一個GC線程。有點眉目了,看到問題的狀況極有多是GC頻繁引發的CPU升高。
咱們執行以下命令,查看全部線程的調用堆棧:
0:000> ~* kb
... 83 Id: 7558.6914 Suspend: 0 Teb: 000007ff`ffe56000 Unfrozen RetAddr : Args to Child : Call Site 000007fe`fd6d10dc : 00000002`2f3cfe10 00000001`5faa1eb0 00000000`11606268 000007fe`fd6d64da : ntdll!ZwWaitForSingleObject+0xa 000007fe`f128d0e0 : 00000000`ffffffff 00000000`ffffffff 00000002`00000000 00000000`00000460 : KERNELBASE!WaitForSingleObjectEx+0x79 000007fe`f128d1e3 : 00000000`00000000 00000000`09e7e190 00000000`00000000 00000000`ffffffff : mscorwks!MethodTableBuilder::MethodSignature::GetMethodAttributes+0xa8 000007fe`f138e250 : 00000000`0b65c640 000007fe`f138e289 0000739d`00000004 00000000`00000001 : mscorwks!CLREvent::WaitEx+0x63 000007fe`f15a9596 : 00000000`00064000 000007fe`f129b6cb ffffffff`fffffffe 000007fe`f1286920 : mscorwks!SVR::gc_heap::wait_for_gc_done+0x80 000007fe`f17339cc : 00000000`00000007 000007fe`f129b6ef 00000001`1ff9e250 00000000`00064930 : mscorwks!SVR::GCHeap::GarbageCollectGeneration+0x206 000007fe`f13996a2 : 00000000`00000000 00000000`0b65c810 00000000`00064930 000007fe`00000007 : mscorwks!SVR::gc_heap::try_allocate_more_space+0x1ac 000007fe`f136ecef : 00000000`00000002 00000000`00000000 00000000`00000038 00000000`00000038 : mscorwks!SVR::gc_heap::allocate_more_space+0x32 000007fe`f12d7278 : 00000000`00000038 00000000`00000038 00000000`09e7e1f8 000007fe`f12d71b1 : mscorwks!SVR::gc_heap::allocate_large_object+0x5f 000007fe`f12d758e : 00000000`010b25e0 00000002`838d92c8 00000000`0006492c 00000000`0003248a : mscorwks!SVR::GCHeap::Alloc+0x198 000007fe`f17b9433 : 00000001`7f8ce378 00000000`09e7e190 00000000`0b65c9a8 00000000`09e7e190 : mscorwks!SlowAllocateString+0x7e 000007fe`f051beec : 00000000`00032489 00000000`00000000 00000000`00000000 00000000`00000002 : mscorwks!FramedAllocateString+0xd3 000007ff`00cf0313 : 00000001`5f3ecd98 00000001`1ff9e098 00000002`55400368 00000000`00000000 : mscorlib_ni+0x2ebeec 000007ff`012b0ac5 : 00000001`5f3ecd98 00000001`5f418988 00000001`ff350370 00000002`4f89cd50 : 0x7ff`00cf0313
...
這時輸出的內容可能很是多,可是不要緊,咱們關注的只是哪一個線程引發的GC,咱們能夠在返回中檢索一下GarbageCollectGeneration就OK了,這個是GC的入口函數。
檢索到是83號線程,那麼爲何83號線程會觸發GC呢?咱們切換到線程83。
0:000> ~83s ntdll!ZwWaitForSingleObject+0xa: 00000000`776712fa c3 ret
查看線程的調用堆棧:
0:083> !clrstack OS Thread Id: 0x6914 (83) Child-SP RetAddr Call Site 000000000b65cae0 000007ff00cf0313 System.String.Concat(System.String, System.String, System.String) 000000000b65cb40 000007ff012b0ac5 xxx.TE.Web.Service.Management.CompositeFieldService.GetCompositeFieldContent(System.Web.HttpContextBase, System.String, TE.Web.Domain.Management.CompositeField, Int32) 000000000b65cfd0 000007ff0126d39b TE.Web.Areas.Cases.Controllers.WritDataBagController.PageWritContent(System.String, Int32) 000000000b65d410 000007fee7ca082c DynamicClass.lambda_method(System.Runtime.CompilerServices.ExecutionScope, System.Web.Mvc.ControllerBase, System.Object[]) 000000000b65d460 000007fee7ca31ef System.Web.Mvc.ReflectedActionDescriptor.Execute(System.Web.Mvc.ControllerContext, System.Collections.Generic.IDictionary`2<System.String,System.Object>) 000000000b65d4c0 000007fee7ca47b5 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(System.Web.Mvc.ControllerContext, System.Web.Mvc.ActionDescriptor, System.Collections.Generic.IDictionary`2<System.String,System.Object>) 000000000b65d500 000007fee7ca354f System.Web.Mvc.ControllerActionInvoker+<>c__DisplayClassd.<InvokeActionMethodWithFilters>b__a() 000000000b65d560 000007fee7ca3780 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(System.Web.Mvc.IActionFilter, System.Web.Mvc.ActionExecutingContext, System.Func`1<System.Web.Mvc.ActionExecutedContext>) 000000000b65d5f0 000007fee7ca3186 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(System.Web.Mvc.ControllerContext, System.Collections.Generic.IList`1<System.Web.Mvc.IActionFilter>, System.Web.Mvc.ActionDescriptor, System.Collections.Generic.IDictionary`2<System.String,System.Object>) 000000000b65d650 000007fee7ca9f5d System.Web.Mvc.ControllerActionInvoker.InvokeAction(System.Web.Mvc.ControllerContext, System.String) 000000000b65d6f0 000007fee7c9b8f0 System.Web.Mvc.Controller.ExecuteCore() 000000000b65d750 000007fee7c9c245 System.Web.Mvc.MvcHandler+<>c__DisplayClass8.<BeginProcessRequest>b__4() 000000000b65d790 000007fee7cd4c7f System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass1.<MakeVoidDelegate>b__0() 000000000b65d7d0 000007fee7c9c035 System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass8`1[[System.Web.Mvc.Async.AsyncVoid, System.Web.Mvc]].<BeginSynchronous>b__7(System.IAsyncResult) 000000000b65d800 000007fee7c9b7e3 System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncResult`1[[System.Web.Mvc.Async.AsyncVoid, System.Web.Mvc]].End() 000000000b65d840 000007feecab5576 System.Web.Mvc.MvcHandler.EndProcessRequest(System.IAsyncResult) 000000000b65d870 000007feeca78867 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 000000000b65d920 000007feed16e8f1 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef) 000000000b65d9c0 000007feed15ef52 System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Exception) 000000000b65db50 000007feed140749 System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext, System.AsyncCallback) 000000000b65dba0 000007feed2713a1 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest, System.Web.HttpContext) 000000000b65dcc0 000007feed270f6b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32) 000000000b65de40 000007feed270e34 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32) 000000000b65dea0 000007fef141b08a DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32) 000000000b65e6d0 000007feed2714d0 DomainNeutralILStubClass.IL_STUB(IntPtr, System.Web.RequestNotificationStatus ByRef) 000000000b65e7b0 000007feed270f6b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32) 000000000b65e930 000007feed270e34 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32) 000000000b65e990 000007fef141b2db DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)
發現到String.Concat就沒了,從代碼猜想,多是程序在作字符串拼接,申請內存不到,因此纔會觸發強烈的GC操做。咱們來看看該線程上的內存分配:
0:083> !dso OS Thread Id: 0x6914 (83) RSP/REG Object Name 000000000b65c470 000000022f3cfe10 System.Web.HttpApplication+PipelineStepManager 000000000b65c8a8 00000002838d92c8 System.String 000000000b65ca10 000000014f620fe0 System.String 000000000b65ca18 00000002838d92c8 System.String 000000000b65ca20 000000014f621260 System.String 000000000b65ca38 000000022f3cfe10 System.Web.HttpApplication+PipelineStepManager 000000000b65ca98 000000022f3cfe10 System.Web.HttpApplication+PipelineStepManager 000000000b65caa0 000000022f3cfe10 System.Web.HttpApplication+PipelineStepManager 000000000b65cab0 00000001dfc47d28 System.String 000000000b65cab8 000000014f621260 System.String 000000000b65cad0 000000014f620fe0 System.String 000000000b65cb00 000000014f620fe0 System.String 000000000b65cb30 000000014f620fe0 System.String 000000000b65cb40 000000015f3ecd98 TE.Web.Service.Management.CompositeFieldService 000000000b65cb98 0000000287523340 System.String 000000000b65cba0 000000024f89d1f0 System.Collections.Generic.List`1[[xxx.TE.Web.Domain.Management.CompositeField, xxx.TE.Web.Domain]] 000000000b65cbb8 000000024f89d218 TE.Web.Domain.Management.CompositeField 000000000b65cbc0 000000024f89db28 System.Text.RegularExpressions.Regex 000000000b65cbc8 000000024f89e028 System.Text.RegularExpressions.MatchCollection 000000000b65cbd0 000000011fb05380 System.Collections.Hashtable 000000000b65cbd8 000000011fb05238 System.String 000000000b65cbe0 000000011fb05288 System.String
...
因爲線程堆棧是執行到字符串就中止了,因此咱們找幾個字符串試試運氣,從最上面(最後)分配的開始:
0:083> !objsize 00000002838d92c8 sizeof(00000002838d92c8) = 411936 ( 0x64920) bytes (System.String)
400多K的字符串,打開來看下這個字符串裏面是些什麼?
0:083> !do 00000002838d92c8 Name: System.String MethodTable: 000007fef0667d90 EEClass: 000007fef026e560 Size: 411932(0x6491c) bytes (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) String: 粵A87xxx,粵ACxxx掛...... Fields: MT Field Offset Type VT Attr Value Name 000007fef066f000 4000096 8 System.Int32 1 instance 205954 m_arrayLength 000007fef066f000 4000097 c System.Int32 1 instance 205953 m_stringLength 000007fef06697d8 4000098 10 System.Char 1 instance 7ca4 m_firstChar 000007fef0667d90 4000099 20 System.String 0 shared static Empty >> Domain:Value 000000000112bef0:00000001ff350370 000000000372e6e0:00000001ff350370 << 000007fef0669688 400009a 28 System.Char[] 0 shared static WhitespaceChars >> Domain:Value 000000000112bef0:00000001ff350b60 000000000372e6e0:00000001ff3588e8 <<
作到這裏,本次系統變卡的緣由貌似已經有一個說法已經說得過去了,就是Asp.net嘗試分配一個大對象,.Net中超過85Kb就叫大對象。可是內存不足,因而觸發GC回收,回收不到一個400Kb長的內存塊,因而報了一個內存溢出異常。
這個道理乍一看,彷佛說得通,可是實際上很詭異,.Net上只是使用了2G左右的內存,一共24G內存的服務器爲何只是用了2G內存,GC拼命回收垃圾呢?爲何只是用了2G內存就分配不出400KB的內存了呢?
在這個地方,卡住了,中途覺得是內存頻繁分配形成大量碎片,往內存碎片方向跟過,最後肯定與內存碎片無關。緣由是不相信這個東西。由於服務器是24核CPU,24G內存。24G內存使用了2G內存就出現這個問題。
這個問題想了兩天,百思不得其解。後來,又無心中發現了一個小苗頭。
!dumpheap -stat
0:000> !dumpheap -stat
------------------------------
…
000007fef0668168 1 136 System.OutOfMemoryException
…
000007fef066f8b0 67392 46915920System.Collections.Hashtable+bucket[]
000007fef0667d90 291003 96055608 System.String
Total 2737406 objects
Fragmented blocks larger than 0.5 MB:
Addr Size Followed by
000000014f92ff10 4.2MB 000000014fd5c698 System.String
000000019fb89940 6.3MB 00000001a01d57b8 System.Threading.OverlappedData
000000020fb18698 5.7MB 00000002100ccc70 System.Threading.Overlapped
在上千行的返回結果中,竟然無心中瞟到一個System.OutOfMemoryException。熟悉C#的應該都知道,這個是內存溢出異常,當內存不足時纔會發生。
原本說分配不出400KB內存我還很懷疑,如今又無心中從上千行的返回結果中瞟到這一行代碼。肯定是內存問題,因而懷疑是Asp.net配置文件不正確,致使系統最多隻可以用到2G內存呢?由於Asp.net Web.Config裏面的processModel節點有個memoryLimit參數,能夠設置Asp.net可以使用的最大內存。可是從網站一直跟到服務器的配置文件,都沒有發現有設置該參數,也就是說,Asp.net使用默認設置是60%。24G*60%最大是可以使用14G左右的內存。 太詭異了,實在想不通。最後,只有碰碰運氣了,隨便輸入些變量看看值有沒有什麼問題。(實際上本處也有章法可依,若是此時找到不問題,最應該就是找到此次請求的參數,重現與當時客戶同樣的操做),當隨手在輸入以下命令以後,忽然眼前一亮。
依次執行以下3個指令:
0:083> !do 000000015f416b08 //換了個DUMP,原本這個地址是出如今 !dso命令裏的 Name: System.Web.HttpContext MethodTable: 000007feecc075c8 EEClass: 000007feec862438 Size: 336(0x150) bytes (C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll) Fields: MT Field Offset Type VT Attr Value Name 000007feecc0a0f0 4000fe6 8 ...IHttpAsyncHandler 0 instance0000000000000000 _asyncAppHandler 000007feecc09c20 4000fe7 10...b.HttpApplication 0 instance 000000022f3cc5d0 _appInstance 000007feecc0a160 4000fe8 18....Web.IHttpHandler 0 instance 000000015f418c48 _handler 000007feecc0a5a0 4000fe9 20 ...m.Web.HttpRequest 0instance 000000015f416c58 _request 0:083> !do 000000015f416c58 <Note: this object has an invalid CLASSfield> Name: System.Web.HttpRequest MethodTable: 000007feecc0a5a0 EEClass: 000007feec863d30 Size: 336(0x150) bytes (C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll) Fields: MT Field Offset Type VT Attr Value Name … 000007feecb76150 4001095 c0...tpValueCollection 0 instance 000000015f417a80 _queryString … 0:083> !do 000000015f418ec0 Name: System.String MethodTable: 000007fef0667d90 EEClass: 000007fef026e560 Size: 90(0x5a) bytes (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) String:_=1400639407322&CaseCode=&Page=0 Fields: MT Field Offset Type VT Attr Value Name 000007fef066f000 4000096 8 System.Int32 1 instance 33 m_arrayLength
以上3個命令,分明是找到HttpContext對象,再找到HttpContext對象的Reqest對象,再找到Request對象的QueryString(請求參數)。
留意CaseCode,竟然是Null(因爲我對此係統的邏輯仍是比較熟悉的。知道CaseCode爲Null是不正常的狀況)怎麼可能呢。用戶是怎樣操做出來的呢?
打開系統辦案臺,找到文書隨便點一下。最後終於發現,若是用戶在相應文書上選擇在新窗口中打開(在IE中是鼠標單擊的時候輕輕拖了一下),就可以發起這樣的請求。
So,我直接在線上的系統模仿了幾回這樣的操做,而且牢牢盯着資源監視器。發現,在40分鐘以後(24核24G內存果真不是蓋的),w3wp.exe的內存已經上升到了5G。隨後,系統就變慢,跟以前的症狀如出一轍。
因而,打開本地的代碼,找到相應的代碼段。
//[HttpPost]爲提升性能用GET提交方式 publicActionResult PageWritContent(string CaseCode, intPage) {…}
此處設置爲了容許Get請求,這沒什麼不妥,可是當我模擬用戶操做的時候,進一步跟蹤發現,當CaseCode爲Null的時候,系統的SQL語句以下:
select xxx,xxx,xx,xxx,xxxfrom xxx where 1 = 1 and (xxx = 1 or xxx = 0) and xxx = 0
這個SQL語句至關於把整個數據庫的案件都查出來了。更重要的是,還關聯查出了xxx,xxx,xxx,xxx,xxx,xxx,xxx等7張關聯表的數據。說白了就是,把系統這幾年的數據都翻出來了。因爲數據比較敏感,把表名,字段之類的都用xxx代替了。
當在本地執行相同操做時,Visual Studio輸出欄拼命地輸出查詢語句數千行。
整個問題的前因後果是:因爲代碼邏輯不嚴謹,致使當缺乏參數時,把整個數據庫的數據都查詢出來,內存不足,.Net的垃圾回收機制啓動,盡職盡責的GC努力回收內存,致使CPU飆升,系統運行變慢,甚至卡死,形成客戶端長時間無響應。同時,服務器內存是24G,分配給.Net應用程序池更少(不可能把整個數據庫的案件所有讀取到內存中),致使不管GC如何努力,最終都仍是報出了一個OutMemoryException異常。問題終於水落石出。
這個Bug比較隱晦。主要是引發Bug的操做並不會當即看到效果,而是過一、2個小時纔會出現變卡的狀況,因此很容易過了測試。而在調試階段,若是程序員不是特別留意那幾個頁面,也很難會留意到這種問題。
不得不說,經過抓DUMP來找到系統問題,這的確是一門手藝活。可以發現隱藏的比較深的Bug。
檢查過同類型的文書,發現有近一半的文書都是容許以Get方式加載文書,同時又不檢查案件編號是否爲空的狀況,例如xxx文書,xxx文書等。所以,修正以下代碼邏輯,在加載文書時,檢查案件編號是否爲空,當案件編號爲空的時候,直接返回。而Get方式加載邏輯上並無問題,保留也可。
至此,暫時已經解決系統忽然變慢的一個問題,下一步就是把代碼更新上服務器以後觀察一段時間,看還會不會出現有高CPU或內存溢出的其餘問題。