我們的查看對象之旅,還遠沒有結束。繼續按照前前一篇的代碼來扒皮。上節講的是通過!dso,找到所有stack上的東西,下面我們看heap上的東西。首先,整體上先來瞭解一下。
我們這裡會用一個新命令,叫做!eeheap(歎號+eeheap)。它有兩個參數,一個叫-gc,一個叫-loader。後者,葡萄在他的書裡面寫過一個case,因為load的東西太多,造成了記憶體巨量的片段,導致OOM出現。對於前者,會簡單的把gc的狀態給我們列出來。當然,你也可以不加參數。我們先看!eeheap -gc
0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x01521018
generation 1 starts at 0x0152100c
generation 2 starts at 0x01521000
ephemeral segment allocation context: none
segment begin allocated size
004cccc0 790d8620 790f7d8c 0x0001f76c(128876)
01520000 01521000 01577ff4 0x00056ff4(356340)
Large object heap starts at 0x02521000
segment begin allocated size
02520000 02521000 02523250 0x00002250(8784)
Total Size 0x789b0(494000)
------------------------------
GC Heap Size 0x789b0(494000)
幾點注意的。
1、GC有三個generation,分別是0、1、2
2、整個gc heap的size是:494000,大概50k左右。
3、看我們抓到的那個dump檔案,大小是55,252,827位元組,55K左右。說明大部分都是託管資源佔用的記憶體。
這個命令大家先記著點,我們看另外一個命令:!dumpheap。這個命令會把託管堆上所有的東西都扒出來。so,我就不把輸出放到這裡了。我們只看最後幾行:
79104368 388 9312 System.Collections.ArrayList
7912d8f8 1164 60448 System.Object[]
7912dae8 55 85940 System.Byte[]
790fd8c4 2581 224408 System.String
Total 7097 objects
託管堆上一共7097個對象,呵呵,吃驚吧?我們那麼點代碼,居然有這麼多東西!(很多東西不是咱自己的。。。),其中字串一共佔用了224408個位元組,就是22k左右,夠大的了!
!dumpheap有幾個參數,都很有用,我們分別來看。(繼續用協助,如果你勤快的話:!help dumpheap)
首先看第一個,-stat參數。顧名思義,它會按照託管資源的類型group by,每個不同的類型的資訊都列出來。下面粘一點內容出來:
0:000> !dumpheap -stat
total 7097 objects
Statistics:
MT Count TotalSize Class Name
791334a8 1 12 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
7911cd80 1 12 System.Collections.Hashtable+KeyCollection
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
7910f2ac 4 240 System.Reflection.AssemblyName
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
79104368 388 9312 System.Collections.ArrayList
7912d8f8 1164 60448 System.Object[]
7912dae8 55 85940 System.Byte[]
790fd8c4 2581 224408 System.String
Total 7097 objects
從這個輸出,我們能看到,-stat是按照TotalSize來排序的(但是這個size不準,一定要注意!!!)。如果我們想看上面的那個System.Reflection.AssemblyName
,怎麼看呢?注意最左面的列,是method table,中間是個數。那麼我們要看一下,這裡就要用另外一個參數了,-mt,後面跟上上面的methodtable。如:
0:000> !dumpheap -mt 7910f2ac
Address MT Size
0155c7a0 7910f2ac 60
0155c928 7910f2ac 60
0155cf00 7910f2ac 60
0155d058 7910f2ac 60
total 4 objects
Statistics:
MT Count TotalSize Class Name
7910f2ac 4 240 System.Reflection.AssemblyName
Total 4 objects
一共有4個object,地址分別是0155c7a0、0155c928、0155cf00和0155d058 ,利用上面一篇的!do,連續幾次,就能看到東西了。
一般來講,!dumpheap -stat和!dumpheap -mt <methodtable>是很有用的,尤其在我們分析high cpu/ high memory的時候。這個以後拿具體例子來講。
我們繼續看!dumpheap的第三個參數 -min,這個命令的好處是,只列出比指定大小大的對象出來。如,我們想看一下大小在8000個位元組以上的對象,那麼可以這麼看:
0:000> !dumpheap -min 8000
Address MT Size
790ddd94 790fd8c4 9280
01522c20 7912dae8 21200
01527ef0 790fd8c4 42396
01532504 7912d8f8 8208
0153d174 7912dae8 38940
0156ae60 7912dae8 9012
total 6 objects
Statistics:
MT Count TotalSize Class Name
7912d8f8 1 8208 System.Object[]
790fd8c4 2 51676 System.String
7912dae8 3 69152 System.Byte[]
Total 6 objects
一共有6個對象,其中1個object數組,2個字串,3個byte數組。比較奇怪,兩個字串怎麼這麼大?居然51k大小?看一下啊,!dumpheap -mt 790fd8c4 -min 8000
0:000> !dumpheap -mt 790fd8c4 -min 8000
Address MT Size
790ddd94 790fd8c4 9280
01527ef0 790fd8c4 42396
total 2 objects
哦,第二個居然佔了42k,真是暈倒!看一下,!do -nofields 01527ef0 ,部分輸出如下:
String: <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<mscorlib>
<security>
<policy>
<PolicyLevel version="1">
<SecurityClasses>
<SecurityClass Name="AllMembershipCondition"
一共42k,很長地。。。這是一個xml文檔,應該是CLR自己維護的。
還有一個參數,-type,這個可以指定我們要看的東西。如string,如hashtable等。可以這麼看:!dumpheap -type String,這裡注意的是,String嚴格對應.NET裡面的資料類型,so,是大小寫敏感的。同理,hashtable你要寫成:!dumpheap -type Hashtable。
這部分字數少了點,不過寫的也比較累。下面我們做一個總結,利用這幾個命令,先來看一個實際問題。