1. 在IIS裏訪問AllProducts.aspx啓動w3wp.exe進程。 oop
2. 使用TinyGet執行 tinyget srv:localhost –uri:/buggybits/AllProducts.aspx –threads:5 –loop:1 ui
3. 在WinDbg目錄執行 adplus –hang –pn w3wp.exe –quiet 線程
4. 使用WinDbg打開第三步生成的.dmp文件,執行 .loadby sos.dll mscorwks orm
5. 執行 !threadpool 查看當前生成dmp時的CPU利用率 : 對象
可見當前CPU利用率爲100% blog
6. 執行 !runaway查看沒有線程佔用的CPU時間: 進程
可見佔用CPU時間最多的是線程18。 字符串
7. 切換到該線程,執行~18s。而後執行 !clrstack 查看該線程執行到了什麼地方: get
可見程序在Page_load方法裏卡住了。找到這條指令所在地址 075f09c9 string
8. 執行 !u 075f09c9進行反彙編:
懷疑程序是在進行字符串構造。
9. 執行 !dso 查看該線程相關的object,找到大量的字符串對象。
10. 查看程序源代碼AllProducts.aspx.cs:
可見程序直接使用了 + 進行字符串鏈接。這正好與System.String.Concat方法契合。
因爲直接將字符串進行相連,會致使以前分配字符串對象的存儲空間被廢棄,CLR必須爲新的字符串對象分配新的空間,這就致使CLR忙於分配字符串空間,形成high CPU。好比下例:
class Program
{
static void Main(string[] args)
{
string s = null;
for (int i = 0; i < 100000; i++)
{
s = s + i.ToString();
}
}
}
運行以後能夠直接在Task Manager裏看到CPU佔用率的直線上升。
對此的解決辦法是使用StringBuilder:
class Program
這樣當有新的String聲稱以後,舊有的string會在原對象大小的基礎分配一個大小爲原對象大小兩倍的對象。這樣就避免CLR反覆消耗於分配新的對象。
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++)
{
sb.Append(i.ToString());
}
}
}
20110218更新:
1、 若是懷疑是High CPU,則使用!threadpool查看究竟多高,再使用!runaway查看哪一個thread佔用的CPU最高,第三步使用!threads查看各個託管thread的類型。
2、使用Performance Monitor的.CLR Memory對象和Process對象,進行監測,查看的counter包括#Gen 0/1/2 collections,Induced GC,Processor Time和Time in GC。獲得以下圖像:
1. 從Processor Time能夠看到CPU利用率獲得100%,而從%Time in GC(途中白色部分)能夠得知GC垃圾回收過程佔用了大部分的CPU使用。GC的平均利用率在65%,可是正常狀況下應該不會超過5%。
2. Gen 0/1/2 Collections的數量基本相等,比值約爲1:1:1;可是正常狀況下,其比值應約爲100:10:1。
3. 致使Gen 0/1/2 Collections對象數量近似的緣由只有一個,那就是GC回收過程被反覆觸發。其中觸發有兩種狀況,一種是手動執行GC回收,另一種是在Gen0中的對象太大,致使Gen0空間很快不足,GC被觸發,成了Gen1,而Gen1很快又給弄成了Gen2,因此三代對象基本相等。
4. 從Induced GC能夠得知並無手動觸發存在,從而斷定是Object太大。
3、切換到CPU佔用率高的線程,執行!clrstack,發現該線程在作以下工做:
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 79378b9f System.String.ConcatArray(System.String[], Int32)
0252f28c 79378af4 System.String.Concat(System.Object[])
0252f2a0 0fe90a2a AllProducts.Page_Load(System.Object, System.EventArgs)
0252f2d8 66f12980 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 6614d80f System.Web.UI.Page.ProcessRequest()
0252f588 6614d72f System.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)
0252f8d8 79f047fd [ContextTransitionFrame: 0252f8d8]
0252f90c 79f047fd [GCFrame: 0252f90c]
0252fa68 79f047fd [ComMethodFrame: 0252fa68]
於是懷疑這個巨大的object是System.String類型。
執行命令 !dumpheap -type System.String -min 85000:
找到heap中相關的string對象,經過!do查看其內容,即可得知程序是在構造一個超長的字符串。