• 查找CLR对象内存所属关系


     

    .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 PagesConfiguration Properties条目中的Debugging项中,打开Enable Unmanaged Debugging(设置为True)。

    然后我们选择Visual Studio .NETDebug菜单,并且选择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个字节。在我们的程序中有一个循环,创建了10StringHolder,因此在test.StringHolder一行中,注明了test.StringHolder10个实例,总共占用120字节内存。

    最后,我们看到有28String,可是在我们的程序中至多只有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


    可以看到ArrayList012919d4,也就是TestClass连接。这个对象就是我们定义在Main()中的那个testClass

  • 相关阅读:
    C# 读写Excel(NPOI库)
    sql server存储过程回滚事务
    sql server的循环语句
    NopCommerce的autofac的理解
    Django——django连接mysql
    bootstrap
    常用模块杂碎小知识
    常识小知识——(1)
    Django——用户注册并展示出注册信息表案例
    Django——orm概述及在django中使用
  • 原文地址:https://www.cnblogs.com/xlshcn/p/clrmemory.html
Copyright © 2020-2023  润新知