[ASP.NET Debugging BuggyBits讀書筆記] Lab03 Memory

原文來自:http://blogs.msdn.com/b/tess/archive/2008/02/20/net-debugging-demos-lab-3-memory-review.aspx 函數

1. 打開Performance Monitor。在Counter Logs裏添加New Log Settings,命名爲Lab3-Mem。添加的Log對象爲.NET CLR Memory和Process。 oop

2. 使用TinyGet執行命令個 tinyget –srv:localhost –uri:/buggybits/links.aspx –loop:4000 ui

3. 命令執行之後,能夠看到w3wp.exe進程的大小持續增加,而且在tinyeget命令結束之後,程序所站大小仍然不減: 命令行

image

4. 等待一段時間之後,在Performance monitor裏面打開剛纔存儲的日誌,添加Process對象的Virtual bytes和Private Bytes計數器,添加.NET CLR Memory對象的Bytes in all Heaps計數器,將他們的scale調整爲0.00000001: 線程

可見隨着Bytes in All heaps(紫紅)的增加,進程的virtual bytes(白色)和Private Bytes(青色)也在增加。所以能夠斷定是.NET進程的heap的增加帶來了內存消耗的增加,而且內存增加以後沒有回收。 debug

5.  切換到WinDbg目錄在命令行執行 adplus –hang –pn w3wp.exe –quiet 注意此時應該將應用程序池的各類Recycle選項所有關閉。不然可能由於抓取時間太長,w3wp.exe進程關閉,hang dump抓取失敗。 指針

6. 使用WinDbg打開剛纔獲得的dump文件。執行 .loadby sos.dll mscorwks 日誌

7. 執行 !eeheap -gc查看GC的狀況,可見有一個heap,由於是單核。若是是雙核,則會有兩個heap。Heap大小爲700多兆,可見是heap出問題了。 code

image

image

8. 執行 !dumpheap –stat: orm

image

可見System.String佔據了大部分的內存空間。

9. 執行 !dumpheap –type Link並終止輸出。找到其中一個object的地址02eab13c,執行 !do 02eab13c:能夠發現Stringbuilder和System.String都是包含在Link類型中的類型。

image

這就解釋了!dumpheap –stat中看到的佔據heap最多的對象的類型的內存佔用狀況。

10. 執行 !dumpheap –type System.String 並強行終止輸出。以其中一個對象的地址 027da238爲例,執行 !do 027da238,能夠找到這個string對象存儲的值。

image

11. 爲了找到爲何該對象沒有被回收,查看哪些線程對它還有root,所以執行命令 !gcroot 027da238:

image

可見Finalizer queue對其有引用,所以能夠推斷Finalizer queue尚未對這個對象執行Finalize方法。

12. 執行 !threads找到Finalize queue的線程是 17:

image

13. 執行 ~17s切換到該線程。執行 !clrstack:

image

可見這就是Finalize queue上對該對象的root一直保持着的緣由:線程休眠了。

14. 在App_Code文件夾裏找到哦啊Link.cs:

image

可見該對象規定了Finalize方法,所以會被放到Finalize queue上去。而這個方法裏面,致使執行該方法的Finalize線程休眠了,因此對象一直有引用,內存得不到釋放。

20110218添加:

1、本例被斷定爲High Memory的問題。這樣的問題有三種狀況,一是Manged Memory泄漏,二是Native Memory泄漏,第三是Load Library出問題。對於這三種狀況,能夠經過Performance Monitor來進行基本斷定。其中.NET CLR Memory object的#bytes in all heaps 這個counter,Process Object的Virutual Bytes和Private bytes這兩個counter。

  1. 若是是Managed Memory泄漏,那麼bytes in all heaps,private bytes和virtual bytes會同市上升。

  2. 若是是Native Memory泄漏,那麼bytes in all heaps不會上升,可是private bytes和virtual bytes都會上升。

  3. 若是是Load Library的問題,那麼bytes in all heaps和private bytes都不會上升,可是virutal bytes會上升。

2、本例debug時首先執行了!address -summary 命令,獲得以下結果:


0:000> !address -summary
-------------------- Usage SUMMARY --------------------------
    TotSize (      KB)   Pct(Tots) Pct(Busy)   Usage
   373b7000 (  904924) : 21.58%    85.85%    : RegionUsageIsVAD
   bfa89000 ( 3140132) : 74.87%    00.00%    : RegionUsageFree
    76e6000 (  121752) : 02.90%    11.55%    : RegionUsageImage
     67c000 (    6640) : 00.16%    00.63%    : RegionUsageStack
          0 (       0) : 00.00%    00.00%    : RegionUsageTeb
    144a000 (   20776) : 00.50%    01.97%    : RegionUsageHeap
          0 (       0) : 00.00%    00.00%    : RegionUsagePageHeap
       1000 (       4) : 00.00%    00.00%    : RegionUsagePeb
       1000 (       4) : 00.00%    00.00%    : RegionUsageProcessParametrs
       2000 (       8) : 00.00%    00.00%    : RegionUsageEnvironmentBlock
       Tot: ffff0000 (4194240 KB) Busy: 40567000 (1054108 KB)

-------------------- Type SUMMARY --------------------------
    TotSize (      KB)   Pct(Tots)  Usage
   bfa89000 ( 3140132) : 74.87%   :834e000 (  134456) : 03.21%   : MEM_IMAGE
     95a000 (    9576) : 00.23%   : MEM_MAPPED
   378bf000 (  910076) : 21.70%   : MEM_PRIVATE

-------------------- State SUMMARY --------------------------
    TotSize (      KB)   Pct(Tots)  Usage
   34bea000 (  864168) : 20.60%   : MEM_COMMIT
   bfa89000 ( 3140132) : 74.87%   : MEM_FREE
    b97d000 (  189940) : 04.53%   : MEM_RESERVE

Largest free region: Base 80010000 - Size 7fefa000 (2096104 KB)

從這裏面能夠獲得的信息由:

  1. RegionUsageIsVAD佔有最多的內存份額,它與GCHeap相關。

  2. Pct(Tots)指的是該部分memory佔總共的virtual memory的比例,而Pct(Busy)指的是該部分memory佔Private memory的比例。

3、因爲已經可以斷定是Managed Memory有問題,咱們須要進一步查看是Managed Memory的哪一步有問題,所以執行!eeheap -gc 對GC佔有的memory進行enumerate:


0:000> !eeheap -gc Number of GC Heaps: 2 ------------------------------
Heap 0 (001aa148)
generation 0 starts at 0x32f0639c
generation 1 starts at 0x32ae3754
generation 2 starts at 0x02eb0038
ephemeral segment allocation context: none
 segment    begin allocated     size
001bfe10 7a733370  7a754b98 0x00021828(137256)
001b0f10 790d8620  790f7d8c 0x0001f76c(128876)
02eb0000 02eb0038  06eacb28 0x03ffcaf0(67095280)
100b0000 100b0038  140a4a08 0x03ff49d0(67062224)
18180000 18180038  1c15c650 0x03fdc618(66962968)
20310000 20310038  242eb99c 0x03fdb964(66959716)
28310000 28310038  2c2f04cc 0x03fe0494(66978964)
31190000 31190038  33185ff4 0x01ff5fbc(33513404)
Large object heap starts at 0x0aeb0038
 segment    begin allocated     size
0aeb0000 0aeb0038  0aec0b28 0x00010af0(68336)
Heap Size  0x15fd1310(368907024)
------------------------------
Heap 1 (001ab108)
generation 0 starts at 0x36e665bc
generation 1 starts at 0x36a28044
generation 2 starts at 0x06eb0038
ephemeral segment allocation context: none
 segment    begin allocated     size
06eb0000 06eb0038  0aea58d4 0x03ff589c(67066012)
14180000 14180038  1817eda8 0x03ffed70(67104112)
1c310000 1c310038  202f0550 0x03fe0518(66979096)
24310000 24310038  28304ca8 0x03ff4c70(67062896)
2c310000 2c310038  2fd62a48 0x03a52a10(61155856)
35190000 35190038  372253f4 0x020953bc(34165692)
Large object heap starts at 0x0ceb0038
 segment    begin allocated     size
0ceb0000 0ceb0038  0ceb0048 0x00000010(16)
Heap Size  0x15ab1570(363533680)
------------------------------ GC Heap Size 0x2ba82880(732440704)
 

 4、執行!dumpheap -stat查看heap中各類類型佔用的內存的大小。這裏原文重點解釋了Size的意義,用!dumpheap -stat 獲得的類型的size值得話並不真實,由於有的對象只是包含了一個別的對象的引用而已,於是計算大小的話只會計算指針大小。須要使用!objsize才能計算出真正的大小。

5、因爲對象沒有被回收,是由於他們的引用沒有獲得釋放。所以執行!gcroot <address of an object>查看能夠的對象的引用狀況。

6、在得知對象沒有被引用的緣由是對象正在被Finalize的過程當中時,能夠執行!FinalizeQueue查看這個queue引用了哪些對象。這裏原文強調了爲何對象會被FinalizeQueue引用:只有默認具備析構函數的系統類型或者顯式定義了析構函數的類型的對象,纔會在Garbage Collect的時候,被Finalize Queue引用執行其Destructor的方法。

相關文章
相關標籤/搜索