您的位置:首页 > 其它

查找CLR对象内存所属关系

2007-11-21 16:22 155 查看
在.NET环境下,所有的对象都是通过CLR进行管理,并且由垃圾收集系统来负责回收。我们可以想象得到的是,CLR应当会以某种形式来管理这些对象,并且这些对象与对象之间具有一定的联系。

有一些工具,例如 .NET Memory Profiler等,可以显示出这些关系,但是,可惜的是,这些工具不是免费的。

实际上,Visual Studio .NET本身已经具有这样的功能,只是没有在文档中介绍,并且比上述的工具要复杂一些而已。我们以下面这个小程序来示例如何使用这些功能:

using System;

using System.Collections;

namespace test

{

class StringHolder

{

public string StringData;

public StringHolder(string stringData)

{

StringData = stringData;

}

}

class TestClass

{

public ArrayList Holders;

[STAThread]

static void Main(string[] args)

{

TestClass testClass = new TestClass();

testClass.Holders = new ArrayList();

for (int i = 0; i < 10; ++i)

{

testClass.Holders.Add(new StringHolder("Hello"));

}

Console.ReadLine();

}

}

}

这个程序的功能还是很简单的,我们在 Console.ReadLine()处设置一个断点,然后按下F5编译并运行程序,Visual Studio .NET会在Console.ReadLine()处暂停。需要注意的是,在编译前需要设定项目属性,在Properties Pages的Configuration Properties条目中的Debugging项中,打开Enable Unmanaged Debugging(设置为True)。

然后我们选择Visual Studio .NET的Debug菜单,并且选择Windows->Immediate,这时候会显示一个Command Window – Immediate 窗口。在这个窗口中输入下面文本:

.load sos

这条命令的作用是装入SOS.DLL,这是一个WinDbg的扩展库,用于调试托管代码。

随后输入:

!dumpheap –stat

该命令要求显示程序中所有对象的统计信息,输入该命令后,在Command Window – Immediate窗口中会显示如下信息:

total 61 objects

Statistics:

MT Count TotalSize Class Name

b550ac 1 12 test.TestClass

79c18514 1 20 System.AppDomainSetup

79c20d74 1 24 System.Collections.ArrayList

79c15614 1 32 System.SharedStatics

79c13fc4 2 40 System.Text.StringBuilder

79c14ee4 1 64 System.ExecutionEngineException

79c14dac 1 64 System.StackOverflowException

79c14c74 1 64 System.OutOfMemoryException

79c16e8c 1 80 System.AppDomain

b723f8 3 92 System.Char[]

b55134 10 120 test.StringHolder

79c125c8 28 1936 System.String

14dce0 5 6164 Free

b7209c 5 6328 System.Object[]

Total 61 objects

这些信息包含了很多有用的信息,第一栏,MT表示MethodTable的地址,实际上代表了一种类型的信息,第二栏Count表示该类型的实例数量,第三栏TotalSize表示该类型所有实例总共占用了多少字节的空间,以及最后一栏,Class Name表示了该类型的名字。

因此,我们可以看到,test.TestClass,在整个程序中只有一个实例,并且这个实例占用了12个字节。在我们的程序中有一个循环,创建了10个StringHolder,因此在test.StringHolder一行中,注明了test.StringHolder有10个实例,总共占用120字节内存。

最后,我们看到有28个String,可是在我们的程序中至多只有20个字符串,那么这些字符串又是从哪里来的呢?

我们输入下面这个命令:

!dumpheap -mt 79c125c8

该命令显示MethodTable 79c125c8的详细信息,结果是:

Address MT Size

0129117c 79c125c8 28

012911f0 79c125c8 204

012912bc 79c125c8 20

01291308 79c125c8 40

01291330 79c125c8 76

0129137c 79c125c8 32

0129139c 79c125c8 84

012913f0 79c125c8 80

01291440 79c125c8 188

012914fc 79c125c8 36

01291534 79c125c8 52

01291568 79c125c8 32

01291588 79c125c8 32

012915a8 79c125c8 20

012915bc 79c125c8 32

012915dc 79c125c8 64

0129161c 79c125c8 48

01291660 79c125c8 36

01291684 79c125c8 84

012916d8 79c125c8 52

0129170c 79c125c8 108

01291778 79c125c8 88

012917d0 79c125c8 48

01291800 79c125c8 112

01291870 79c125c8 60

012918ac 79c125c8 204

01291978 79c125c8 48

012919b8 79c125c8 28

Bad MethodTable for Obj at 01291ac0

Last good object: 01291ab4

total 28 objects

Statistics:

MT Count TotalSize Class Name

79c125c8 28 1936 System.String

Total 28 objects

这个表列出了所有字符串的实例,第一栏是实例的地址,第二栏是实例所属MT的地址,第三栏是实例所占的字节数。我们可以通过下面这条命令来显示一个实例的所属关系:

!gcroot 012919b8

该命令要求调试器查找地址012919b8这个对象实例的信息,输出如下:

Scan Thread 1748 (6d4)

ESP:12f2ec:Root:012919e0(System.Collections.ArrayList)->012919f8(System.Object[])->01291ab4(test.StringHolder)->012919b8(System.String)

ESP:12f318:Root:01291ab4(test.StringHolder)->012919e0(System.Collections.ArrayList)

ESP:12f3b0:Root:01291ab4(test.StringHolder)->012919e0(System.Collections.ArrayList)

ESP:12f458:Root:01291ab4(test.StringHolder)->012919e0(System.Collections.ArrayList)

ESP:12f464:Root:01291ab4(test.StringHolder)->012919e0(System.Collections.ArrayList)

Scan Thread 3364 (d24)

Scan HandleTable 1571f8

Scan HandleTable 14a5b0

从这个结果可以得到我们所希望看到的信息:

Root:012919e0是一个ArrayList,从它连接到012919f8,一个Object数组,然后连接到01291ab4,这是一个StringHolder,最后连接到012919b8,我们所查找的字符串。

然后我们来看一下StringHolder的内容。输入下面命令:

!dumpobj 01291ab4

该命令会显示一个对象的内容:

Name: test.StringHolder

MethodTable 0x00b55134

EEClass 0x00f234ec

Size 12(0xc) bytes

mdToken: 02000002 (C:"Documents and Settings"Andrew"My Documents"Visual Studio Projects"test"bin"Debug"test.exe)

FieldDesc*: 00b550f8

MT Field Offset Type Attr Value Name

00b55134 4000001 4 CLASS instance 012919b8 StringData

上面最后两行构成了一个表格,表示这个对象中所有字段的值。在StringHolder中只有一个字段,是StringData,它的值,指向012919b8,也就是上面看到的连接的字符串。

为了找到ArrayList的所属者,我们继续输入:

!gcroot 012919e0

结果是:

Scan Thread 1748 (6d4)

ESP:12f2ec:Root:012919e0(System.Collections.ArrayList)->012919e0(System.Collections.ArrayList)

ESP:12f310:Root:012919e0(System.Collections.ArrayList)->012919e0(System.Collections.ArrayList)

ESP:12f454:Root:012919e0(System.Collections.ArrayList)->012919e0(System.Collections.ArrayList)

ESP:12f45c:Root:012919d4(test.TestClass)->012919e0(System.Collections.ArrayList)

Scan Thread 3364 (d24)

Scan HandleTable 1571f8

Scan HandleTable 14a5b0

可以看到ArrayList被012919d4,也就是TestClass连接。这个对象就是我们定义在Main()中的那个testClass。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: