我们的查看对象之旅,还远没有结束。继续按照前前一篇的代码来扒皮。上节讲的是通过!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。
这部分字数少了点,不过写的也比较累。下面我们做一个总结,利用这几个命令,先来看一个实际问题。