.Net 調式案例—實驗4 高CPU(High CPU)回顧 web
如今開始第四個實驗。 服務器
前面的案例和設置指導 session
請參看前面發佈的文章。 asp.net
再現問題 dom
1) 從新啓動IIS(iisreset) 函數
2) 瀏覽http://localhost/BuggyBits/ AllProducts.aspx,這大概會花費5秒或更多的時間,這取決於你的機器性能。 oop
3) 打開頁面的過程當中,看看任務管理器中的cpu使用量。 性能
Q:在這個請求的處理過程當中,cpu的使用量是怎麼樣的? 測試
A:很高,接近100%。 優化
設置性能計數器日誌
1) 打開一個性能計數器,從開始/運行 中輸入:perfmon.exe。
2) 右鍵在點擊「性能日誌和警告/日誌計數器」上點擊「新建日誌設置」,在彈出的對話框中輸入:Lab3-Mem。
3) 在接下來彈出的對話框中點擊「添加對象…」。
4) 添加對象「.NET CLR Memory」和「Process」,點擊關閉。
5) 把時間間隔設置成1秒,由於咱們這個例子運行的時間比較短。
6) 修改運行方式,把「<默認>」修改爲「domain\username」(把默認這個東西刪除掉,把你機器的用戶名輸入進去),而後點擊設置密碼,把你的用戶名對應的密碼輸入進去。這樣作的目的是你要以管理員(administrator)或具備對w3wp.exe 調試權限的用戶來運行日誌計數器。不然將得不到.net 計數器的值。
7) 點擊確認,這個將會啓動性能監視器對進程的監控。
準備收集內存dump文件
1)打開一個命令窗口,切換到調試器目錄,輸入以下的命令,但不要敲回車:
adplus -hang -pn w3wp.exe -quiet
再現問題並收集dump文件
1)在命令提示符下,切換到TinyGet目錄下,輸入命令,啓動5個線程,每次1個請求。
tinyget -srv:localhost -uri:/BuggyBits/AllProducts.aspx -threads:5 -loop:1
2)當cpu很高的時候,在adplus 的命令中 敲下回車鍵。
3)中止性能監視器。
打開dump文件並看看裏面正在作什麼
步驟和前面實驗中講的同樣,這裏略過。
檢驗一下你是在恰當的時候獲取了這個dump文件
1) 運行 !threadpool 來看看cpu的使用量,確信你在恰當的時候抓取到了這個dump文件。
Q:cpu的使用率是多少?
A:根據命令的輸出,使用率是98%。
0:027> !threadpool
CPU utilization 98%
Worker Thread: Total: 5 Running: 5 Idle: 0 MaxLimit: 200 MinLimit: 2
Work Request in Queue: 4
AsyncTimerCallbackCompletion TimerInfo@001c9990
…..
--------------------------------------
Number of Timers: 7
--------------------------------------
Completion Port Thread:Total: 1 Free: 1 MaxFree: 4 CurrentLimit: 0 MaxLimit: 200 MinLimit: 2
Q:根據進程的cpu使用率的關係,這裏的cpu使用率是怎麼顯示的?
A:這是一個有點矛盾的問題,cpu的使用率顯示的是在該系統上這個進程使用了多少的cpu,或其餘某個進程使用了多少cpu。一般的,在一個專用的web服務器上,你可使用這個來做爲一個參考來指示cpu佔用率是否是很高,但最好的方法仍是查看性能技術器的日誌。
Q:若是你運行tinyget,你可能會看到一些請求隊列,例如:Work Request in Queue: 4,爲何請求會排成隊列呢?爲何他們沒有按計劃的來執行?(和Running worker threads 比較,還有 worker threads 的最大限制)
A:在這裏例子中,我其實運行了tinyget 兩次,我有5個工做線程(worker threads),多有的請求,儘管他們比最大工做線程要小不少(100 線程/1處理器,2處理器=200線程),當cpu使用率超過80%的時候沒有新的線程被啓動。因此在這之後的致使了100%的請求進來後會被放到等待隊列中,等待新的線程被啓動或其餘線程空閒。
查找是那些線程消耗了大部分的cpu資源
1)運行 .time 來看看uptime 和進程的 cpu user time。
0:027> .time
Debug session time: Fri Feb 22 12:26:55.000 2008 (GMT+1)
System Uptime: 8 days 9:17:00.157
Process Uptime: 0 days 0:01:07.000
Kernel time: 0 days 0:00:06.000
User time: 0 days 0:00:45.000
2) 運行 !runaway 來看看全部線程的 usertime。
0:027> !runaway
User Mode Time
Thread Time
18:704 0 days 0:00:17.843
19:9f4 0 days 0:00:13.328
16:1948 0 days 0:00:10.718
26:a7c0 days 0:00:01.375
24:114 0 days 0:00:01.093
27:d54 0 days 0:00:00.390
28:1b70 0 days 0:00:00.328
0:b7c0 days 0:00:00.171
25:3f8 0 days 0:00:00.000
…..
Q:從輸出中,看看是哪些線程消耗了大部分的cpu?
A:看看線程 18 19 和16 號,他們總共消耗了大約42秒的時間
Q:被進程消耗的總的cpu時間是多少?(從 .time 來看)
A:從 .time 命令的輸出看出,進城消耗了大約 1分鐘 7秒的時間,45秒消耗在了用戶模式下,這個時間是兩個cpu的總的有效時間。
注意,看看 !runaway 的輸出是有點滑稽的,有下面幾個理由:
首先,在一個多cpu或多核的cpu的機器上,你必須記住,用戶模式時間(usermode time,花在用戶模式下的時鐘週期)是全部處理器上的cpu的時間的總和,所以 把用戶模式下的線程時間加起來的總和會比 .time 中看到的流逝的時間還要大。
其次,!runaway輸出的是線程啓動後就開始計時的用戶模式時間。在一個asp.net環境中,一個線程會被多個請求拿來重用,因此在一個線程上一個很高的用戶模式的時間並不意味着必定是運行於該線程上的請求致使了高cpu的問題。
最後,在一些線程中,好比GC線程(在多處理器中,serverGC 進程)會在正在的進程週期中保持存在,因此它們有更高的可能性相對其餘工做線程來講,佔用了更多的用戶模式時間。因此查看兩個連續的dump文件,比較用戶模式裏面的線程的不一樣是更加有意義的。
3) 選擇一個有高cpu佔用時間的線程,看看它的調用堆棧。
~#s (切換線程,請把# 換成 !runaway 中實際的線程號)
kb 2000 (查看本地(原生)調用堆棧)
!clrstack (查看 dotnet 調用堆棧)
0:018> kb
ChildEBP RetAddr Args to Child
0290fd90 7d4d8f0e ffffffff 0290fde8 00000000 ntdll!ZwAllocateVirtualMemory+0x12
0290fddc 7d4d8f51 ffffffff 0aec1000 0030d000 kernel32!VirtualAllocEx+0x41
0290fdf8 79e7495b 0aec1000 0030d000 00080000 kernel32!VirtualAlloc+0x18
0290fe38 79e74929 0aec1000 0030d000 00080000 mscorwks!EEVirtualAlloc+0x104
0290fe50 79e7499b7a3b45f0 0aec1000 0030d000
mscorwks!CExecutionEngine::ClrVirtualAlloc+0x14
0290fe6c79fbdce8 0aec1000 0030d000 00080000 mscorwks!ClrVirtualAlloc+0x1a
0290fe88 79fc2efa 0aec0b18 0030d6b0 0aec0b18 mscorwks!SVR::reset_memory+0x3e
0290fea4 79fc2eb6 0aec0b18 0030d6b0 00000001
mscorwks!SVR::gc_heap::make_unused_array+0x1b
0290fec4 79fc3d1d 0aec0b18 0030d6b0001a8970 mscorwks!SVR::gc_heap::thread_gap+0x30
0290fef0 79fc3c15 00000000001a8800 00000002
mscorwks!SVR::gc_heap::sweep_large_objects+0xd4
0290ff28 79efa9ba 00000002 00000000001a8800
mscorwks!SVR::gc_heap::mark_phase+0x3c5
0290ff54 79efaf60 ffffffff 00000000001a8800 mscorwks!SVR::gc_heap::gc1+0x46
0290ff74 79efa72f00000000 00000000001a8800
mscorwks!SVR::gc_heap::garbage_collect+0x246
0290ff98 79fc8583 79fc855979f95b5c79fc857c
mscorwks!SVR::gc_heap::gc_thread_function+0x6a
0290ffb8 7d4dfe21001a8800 00000000 00000000
mscorwks!SVR::gc_heap::gc_thread_stub+0x92
0290ffec 00000000 79fc8538001a8800 00000000 kernel32!BaseThreadStart+0x34
0:016> kb
ChildEBP RetAddr Args to Child
0252f084 7d4d8c82 00000308 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15
0252f0f4 79e789c6 00000308 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xac
0252f138 79e7898f00000308 ffffffff 00000000 mscorwks!PEImage::LoadImage+0x1af
0252f188 79e78944 ffffffff 00000000 00000000 mscorwks!CLREvent::WaitEx+0x117
0252f19c 79fbc82affffffff 00000000 00000000 mscorwks!CLREvent::Wait+0x17
0252f1b0 79fbc6da 00000000 58ec767e 00000000
mscorwks!SVR::GCHeap::WaitUntilGCComplete+0x34
0252f1ec 79fc2945 58ec75d2 00032cff0000909f
mscorwks!Thread::RareDisablePreemptiveGC+0x1a0
0252f240 79fc2b07 00000000 0b376f34 7936d6e7
mscorwks!Thread::RedirectedHandledJITCase+0x13d
0252f2cc66f1298006f00f7006f53d34 6628efd2
mscorwks!Thread::RedirectedHandledJITCaseForGCThreadControl+0x7
0252f2d8 6628efd206f00f70 02ef3cec06f53d24
System_Web_RegularExpressions_ni+0x12980
0252f2ec 6613cb0406f00f70 02ef3cec 02eee62cSystem_Web_ni+0x36efd2
0252f300 6613cb500252f50c02ef3cec 02ef3cec System_Web_ni+0x21cb04
0252f4e86614c717 02ef11ac00000000 02ee35fc System_Web_ni+0x21cb50
0252f50c 6614d8c3 00000001 02eee62c00000000 System_Web_ni+0x22c717
0252f544 6614d80f00000001 06eb01e4 02eb446cSystem_Web_ni+0x22d8c3
0252f580 6614d72f02ef3cec 6614d6c2 00000000 System_Web_ni+0x22d80f
0252f5a0 65fe6bfb 660ee55406f0a36c02eee050 System_Web_ni+0x22d72f
0252f5d4 65fe3f51 00000000 0000006e 00000000 System_Web_ni+0xc6bfb
0252f610 65fe77330252f63806f00110 02eee35cSystem_Web_ni+0xc3f51
0252f664 65fccbfe0252f6b4 02ef0f6c06f00fe8 System_Web_ni+0xc7733
Q:它們在幹什麼?你看這這些,能假設是什麼致使了gaocpu麼?
A:18 和 19 號 是兩個GC 線程(gc_heap::gc_thread_stub),16號是運行託管代碼的,但,當前正在等待GC完成(GCHeap::WaitUntilGCComplete),因此在這點上,16號是空閒的(idle),因此如今看來大部分的cpu是消耗在了GC線程上了。
4) 這個僅適用在多處理器的機器上,由於他們有專用的GC線程,把 !runaway 中的用戶模式下的GC 線程的時間加起來 除以 .time 中的用戶模式的時間。
Q:在用戶模式的時間中,GC線程花費了多少的時間?
A:18 和19 號 線程使用了超過21秒的cpu時間,=> 大約50%的cpu總時間,那是在垃圾收集中花費了很是多的時間。
檢查性能監視器的日誌
1) 打開性能監視器,添加技術器:.NET CLR Memory/% Time in GC, # Gen 0 Collections, #Gen 1 Collections, #Gen 2 Collections 和 Large Object Heap Size。
Q:% Time in GC 這個技術器是衡量什麼指標的?
A:這個計數器是:表示自從最後一個GC回收開始,花費在垃圾收集上的時間佔所流逝的時間的百分比。這個計數器一般是表示應用程序回收合併內存過程當中垃圾收集器所做工做的一個效能指示。在每一個GC運行結束後,這個計數器會更新,計數器的值表明最後一次觀察到的值,它不是平均值。換句話說,在Gen 2 收集以後,曲線確定會有一個躍起,關鍵是這裏,這個GC的時間比較平均起來沒有在很高的地方結束。
Q:在壓力測試中,% Time in GC 的平均值是多少?
A:在我這裏大概是50%-- 60%,這個在很大程度上取決於你是單處理器仍是多處理器,由於 server GC 和workstation GC 是根據這個不一樣而進行不一樣優化的。運行在多處理器上的asp.net 服務中的 Server GC 會作更多徹底的收集,從新分配和合並內存塊,workstation版的會在高內存消耗的時候省略掉一些作法。
Q:% Time in GC的合適的值是多少?
A:這是一個很滑稽的問題,有些人說5 ,有些說是30 ,但我以爲在大多數的asp.net應用中5%是很難達到的,特別是這種高內存使用量的來講。然而,基於經驗,我認爲,若是在一個進程中30%的cpu使用被用來作垃圾收集,那是多麼愚蠢的。雖然這樣說,可是低的% Time in G也是很是狡猾的,由於它會改變應用的內存分配方式的改變。因此說任何的優化,儘可能作到把它調到合適的水平,不要作的過分。
Q:在Gen 0, Gen 1 和 Gen 2 collections 之間的比率是多少?什麼樣的狀況是能夠接受的? 爲何?
A:Gen 0 的收集基本上是不須要消耗什麼的,免費的。Gen 1 的收集(包含Gen 0 的收集)也是很是廉價的,由於咱們仍然使用着不多數量的要分配的或回收的內存等。Gen 2 的收集從另外一方面來講將是很是很是地昂貴的,由於它處理的是整個dotnet 的GC 堆。最理想的比率是 每100次Gen0 的收集,10次的Gen 1 收集,1次的Gen 2 的收集,即:100:10:1 ,在這裏例子中是
5853 Gen 0 Collections
5830 Gen 1 Collections
5430 Gen 2 Collections
這些值說明了,在很是屢次的每一次的收集行爲中,Gen 2 發生了不少次(由於這個頁觸發 Gen 0 和Gen 1 的收集)。這是很是壞的,由於咱們並無使用到GC給咱們帶來的好處,這不是一個通常意義上的GC,而是它老是不停的要尋找整個內存,釋放回收……。
Q:是什麼東西會致使象上面這樣的比率出現?
A:一般狀況下,兩種事情致使這樣的比率。一個是很是頻繁的分配大對象,以至於每次LOH的段都用完了,二是你常常的直接調用GC.Collect方法,或者GC.GetTotalMemory(true) 這樣的方法。若是有人手動的調用了這些方法,那.NET CLR Memory/# Induced GC 這個計數器就會上升,但在這裏,你能夠看見它保持平穩,這就是說,咱們可能咱們在一直不停的申請大對象。
查看dump文件,找出在GC中是什麼致使了高cpu?
1)運行 ~* kb 2000 獲得全部的本地調用堆棧,尋找線程中觸發GC的函數(mscorwks!SVR::GCHeap::GarbageCollectGeneration)
27 Id: bcc.d54 Suspend: 1 Teb: fff06000 Unfrozen
ChildEBP RetAddr Args to Child
103eed84 7d4d8c82 00000314 00000000 00000000 ntdll!ZwWaitForSingleObject+0x15
103eedf4 79e789c6 00000314 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xac
103eee38 79e7898f00000314 ffffffff 00000000 mscorwks!PEImage::LoadImage+0x1af
103eee88 79e78944 ffffffff 00000000 00000000 mscorwks!CLREvent::WaitEx+0x117
103eee9c79efafc5 ffffffff 00000000 00000000 mscorwks!CLREvent::Wait+0x17
103eeec0 79efad3d ffffffff 001ab0e0 001ab188 mscorwks!SVR::gc_heap::wait_for_gc_done+0x62
103eeee8 79efa339 00000000 00000000 001ab0e0 mscorwks!SVR::GCHeap::GarbageCollectGeneration+0x1b5
103eef78 79ef98cf 103eefb4 00047470 00000003 mscorwks!SVR::gc_heap::try_allocate_more_space+0x136
103eef9479f20ee4 103eefb4 00047470 00000003 mscorwks!SVR::gc_heap::allocate_more_space+0x2e
103eefd879f3d20e 00047470 00000000 00000000 mscorwks!SVR::gc_heap::allocate_large_object+0x5a
103eeff4 79e7510e0f468840 00047470 00000000 mscorwks!SVR::GCHeap::Alloc+0x8d
103ef010 79e86713 00047470 00000000 00000000 mscorwks!Alloc+0x60
103ef04c79e865b900023a304a807762 0773cd58 mscorwks!SlowAllocateString+0x29
103ef0f0 79378b7d 0773cd5800023a2f00000008 mscorwks!FramedAllocateString+0xa0
103ef104 79378af4 103ef14c0d36b268 0773cd1cmscorlib_ni+0x2b8b7d
103ef12c7a4a88d6 00000000 031df118 031deef0 mscorlib_ni+0x2b8af4
103ef14c66f1298006f00098 031deaec 6628efd2 System_ni+0x688d6
103ef38c6614d8c3 00000001 03198328 00000000 System_Web_RegularExpressions_ni+0x12980
103ef3c4 6614d80f00000001 06eb01e4 02eb446cSystem_Web_ni+0x22d8c3
103ef400 6614d72f031ddfd8 6614d6c2 00000000 System_Web_ni+0x22d80f
0:016> !clrstack
OS Thread Id: 0x1948 (16)
ESP EIP
0252f208 7d61c828 [RedirectedThreadFrame:0252f208]
0252f24c 79363058 System.String.wstrcpy(Char*, Char*, Int32)
0252f258 7936d6e7 System.String.FillStringChecked(System.String, Int32, System.String)
0252f278 79378b9fSystem.String.ConcatArray(System.String[], Int32)
0252f28c 79378af4 System.String.Concat(System.Object[])
0252f2a0 0fe90a2aAllProducts.Page_Load(System.Object, System.EventArgs)
0252f2d866f12980 System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)
0252f2e8 6628efd2 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)
0252f2f8 6613cb04 System.Web.UI.Control.OnLoad(System.EventArgs)
0252f308 6613cb50 System.Web.UI.Control.LoadRecursive()
0252f31c 6614e12d System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
0252f518 6614d8c3 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
0252f550 6614d80fSystem.Web.UI.Page.ProcessRequest()
0252f588 6614d72fSystem.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
0252f590 6614d6c2 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
0252f5a4 0fe90205 ASP.allproducts_aspx.ProcessRequest(System.Web.HttpContext)
0252f5a8 65fe6bfb System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
0252f5dc 65fe3f51 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
0252f61c 65fe7733 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
0252f66c 65fccbfe System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
0252f688 65fd19c5 System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
0252f6bc 65fd16b2 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)
0252f6c8 65fcfa6d System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)
0252f8d879f047fd [ContextTransitionFrame:0252f8d8]
0252f90c 79f047fd [GCFrame:0252f90c]
0252fa6879f047fd [ComMethodFrame: 0252fa68]
Q:爲何GC 會被觸發?
A:咱們嘗試去分配一個大對象(gc_heap::allocate_large_object),爲了達到目的,咱們須要更多的空間,那就觸發了垃圾收集。那個要分配的對象大小是0x47470 = 291952 bytes,那就是位於LOH結尾處的東西。
Q:它正在分配的是什麼樣類型的對象?
A:基於託管棧,它看起來象是一個 char[] 或是一個string,由於咱們包含strings,它在內部作wstrcpy。
Q:線程正在幹什麼?
A:它正在處理一個AllProducts.aspx頁面的請求,調用了Page_Load 函數,裏面有字符串鏈接。
Q:是這個致使了 Gen 0 ,Gen1 和Gen2 的比率不正常麼?爲何?
A:包含大的字符串會致使高的cpu,這是衆所周知的。關於緣由,我會再寫一篇文章。這裏不深刻討論。若是你在循環中作 str=str1+str2,你會建立一個新的對象,而不是把原來的對象擴大,若是不停的循環,strings會變大,你就不要不停的建立新的strings,那麼一個就比一個大,這就致使LOH段被頻繁的擴大,最終結果致使不少GC的發生和高cpu。
2)找出LOH上面有什麼東西,若是你不幸,你抓到的GG正在計劃或從新分配(plan or relocate)階段,那 !dumpheap 的輸出就會步正確。若是那樣的話,你要!dumpheap -min 85000。
0:016> !dumpheap -min 85000
The garbage collector data structures are not in a valid state for traversal.
It is either in the "plan phase," where objects are being moved around, or
we are at the initialization or shutdown of the gc heap. Commands related to
displaying, finding or traversing objects as well as gc heap segments may not
work properly. !dumpheap and !verifyheap may incorrectly complain of heap
consistency errors.
------------------------------
Heap 0
AddressMTSize
03481a14001a86d8 136876 Free
0aec0b18001a86d8 3200688 Free
0b1ce1c8 790fd8c4 246832
0b20a608 790fd8c4 246992
0b246ad8 790fd8c4 462032
0b2b77a8001a86d8 784256 Free
0b376f28 790fd8c4 416272
0b3dc938001a86d8 336960 Free
0b42ed78 7912dae8 1048592
0b52ed88 790fd8c4 416432
total 10 objects
------------------------------
Heap 1
AddressMTSize
object 06eb0038: does not have valid MT
curr_object : 06eb0038
----------------
0ceb0048001a86d8 1180896 Free
0cfd0528 790fd8c4 590672
0d060878001a86d8 3189232 Free
0d36b268 790fd8c4 291792
total 4 objects
------------------------------
total 14 objects
Statistics:
MT Count TotalSize Class Name
7912dae8 1 1048592 System.Byte[]
790fd8c4 7 2671024 System.String
001a86d8 6 8828908 Free
Total 14 objects
Q:LOH上面有什麼?
A:在這裏,由於咱們在GC的裏面,因此咱們不能真的橫貫GG 堆,這就是說 這個命令輸出中沒有把全部的大對象顯示給咱們,但根據咱們的理論,始終如一的主要的是Byte[] 和Strings ,它們包含字符串鏈接。
Q:在字符串的大小裏面是否是有什麼規律?
A:僅僅在這裏能夠作爲一個證據,並非在全部的案例中均可以。這裏有一對stirngs string(246832 bytes and 246992 bytes)和另一對strings stings (416272 and 416432 bytes),它們的大小相差160Bytes。若是你剛好獲得一個dump文件,堆已經中止運轉,你會看到一個很長的strings 的列(list)。這些strings你們不斷的以必定的數字增長,這個基本上是由於每一個都不斷地和那個數字長度的string鏈接。
2)把一些strings 輸出來看看內容
0:016> !do 0b1ce1c8
Name: System.String
MethodTable: 790fd8c4
EEClass: 790fd824
Size: 246832(0x3c430) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
<table><tr><td><B>Product ID</B></td><td><B>Product Name</B></td><td><B>Description</B></td></tr><tr><td>0</td><td>Product 0</td><td>Description for Product 0</td>
</tr><tr><td>1</td><td>Product 1</td><td>Description for Product 1</td></tr><tr><td>2</td><td>Product 2</td><td>Description for Product 2</td></tr><tr><td>3</td>
<td>Product 3</td><td>Description for Product 3</td></tr><tr><td>4</td><td>Product 4</td><td>Description for Product ...
Q:基於棧,垃圾收集的知識,和字符串的內容,你能不能找到哪裏的代碼有問題?
A:前面提到過是Page_load 的裏面有字符串相鏈接。致使了高cpu。這個代碼看起來像是組織一個產品的表格。
檢查代碼,證明你的假設
1) 打開AllProducts.aspx.cs 看看代碼
protected void Page_Load(object sender, EventArgs e)
{
DataTable dt = ((DataLayer)Application["DataLayer"]).GetAllProducts();
string ProductsTable = "<table><tr><td><B>Product ID</B></td><td><B>Product Name</B></td><td><B>Description</B></td></tr>";
foreach (DataRow dr in dt.Rows)
{
ProductsTable += "<tr><td>" + dr[0] + "</td><td>" + dr[1] + "</td><td>" + dr[2] + "</td></tr>" ;
}
ProductsTable += "</table>";
tblProducts.Text = ProductsTable;
}
Q:怎麼解決?爲何這樣能解決問題?
A:使用StringBuilder 來附加數據,而不是使用字符串鏈接。由於string builder 有一個緩衝區,它能夠放字符串,不用每次都建立新的對象,當它須要擴展緩衝區的時候,它會加倍的申請,隨着100 ,1000的因子不一樣,而預分配緩衝區,因此應用程序須要在LOH分配新的內存的次數就會下降。若是你使用了stringBuilder你會發現cpu使用率就降下來了,CC調用的次數也會降下來,你會發現垃圾收集的次數就不是5000屢次而是少於10次徹底的收集。