none
new关键字 RRS feed

答案

  • 你好!
         准确的来说,引用类型的对象的实例是保存在托管堆中的,其中包括这个类的成员字段(如果是引用类型的程序字段,保存的是引用,如果是值类型的字段,保存的是值本身),此外,还有一些隐藏的成员字段,用于特殊的目的:
         MethodTable Ptr(方法表指针):用于方法调用
         Sync Block Index:(同步阻塞索引):用于线程的同步
         一个内部的字段(名字没有公开):用于计算对象实例的Hash值,每创建一个新的对象,这个值自动加1,然后就不能更改了,以保证Hash值的唯一性!
         一般后面的内容作为了解就可以了!希望这个解释对你有帮助!
    周雪峰
    2009年10月7日 5:25
    版主
  • 你好!
         深入理解值类型和引用类型,这是.NET开发人员取得长期成功的关键,下面从内存布局角度详细给大家说明一下值类型和引用类型:
         值类型的内存结构:

         引用类型的内存结构:

         
    引用类型的实例比值类型的实例多了两个附加的字段,SyncBlockIndex和RTTI(运行时类型信息)指针,指向一个方法表结构,所以描述为MethodTablePtr和TypeObjectPtr都是可以的,〈CLR Via C#〉这本书的第一版标注的是MethodTablePtr,第二版标注的是TypeObjectPtr,这里应该是Jeffery认为TypeObjectPtr更加准确。
    另外,除Object和ValueType类型以外,值类型不能继承其他任何类型,同时也无法作为其他值类型的基类,所以一般情况下,也就无需方法表指针了!需要调用调用从Object或ValueType继承的一些方法的时候,需要一个方法表指针,这时就需要对值类型进行装箱!
    看一个例子:
    public struct A
    {
         public overrid String ToString()
         {
                 return "A++";
          }
    }

    static void Main(string[] args)
    {
           A a=new A();
           a.ToString();//这时不会发生装箱,因为C#编译器发现结构体A重写了ToString方法,于是产生直接调用ToString的IL指令,而且不可能有类型继承自结构体A,所以编译器可以确定这里不会有多态性的行为,于是就无须方法表指针,也无须装箱!
           a.GetType();//这里会发生装箱,因为结构体A本身没有实现GetType方法,需要调用继承的GetType方法,这时需要一个方法表指针来访问方法表,这时需要通过装箱来获得!


    周雪峰
    2009年10月9日 10:19
    版主

全部回复

  • 最先是对象实例 链接字符串 次要的是 事务 连接池 等
    2009年10月6日 12:11
  • SqlConnection对象的所有东西。
    【孟子E章】
    2009年10月6日 13:08
    版主
  • 您好,
    就每个托管对象实例而言都包含:Type object pointer and Sync block index。
    此外就con而言,还应包含所有已初始化的成员域。可以参考微软源代码看看都为哪些成员初始化了。
    2009年10月6日 15:45
  • 这个得看CLR怎么解释,CLR不是只有微软一种,开源的CLR也有很多。

    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2009年10月6日 16:14
    版主
  • 申请的内存空间里 应该包括这个类的实例初始化后的属性数据。
    另外可能还有其他对象实例的引用地址等信息。
    具体可以看看msdn,或者你反射查看一下这个类的代码。
    http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqlconnection(VS.80).aspx
    Frank Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
    欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
    欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum
    2009年10月7日 4:54
    版主
  • 你好!
         准确的来说,引用类型的对象的实例是保存在托管堆中的,其中包括这个类的成员字段(如果是引用类型的程序字段,保存的是引用,如果是值类型的字段,保存的是值本身),此外,还有一些隐藏的成员字段,用于特殊的目的:
         MethodTable Ptr(方法表指针):用于方法调用
         Sync Block Index:(同步阻塞索引):用于线程的同步
         一个内部的字段(名字没有公开):用于计算对象实例的Hash值,每创建一个新的对象,这个值自动加1,然后就不能更改了,以保证Hash值的唯一性!
         一般后面的内容作为了解就可以了!希望这个解释对你有帮助!
    周雪峰
    2009年10月7日 5:25
    版主
  • 你好!
         准确的来说,引用类型的对象的实例是保存在托管堆中的,其中包括这个类的成员字段(如果是引用类型的程序字段,保存的是引用,如果是值类型的字段,保存的是值本身),此外,还有一些隐藏的成员字段,用于特殊的目的:
         MethodTable Ptr(方法表指针):用于方法调用
         Sync Block Index:(同步块索引):用于线程的同步
         一个内部的字段(名字没有公开):用于计算对象实例的Hash值,每创建一个新的对象,这个值自动加1,然后就不能更改了,以保证Hash值的唯一性!
         一般后面的内容作为了解就可以了!希望这个解释对你有帮助!
    周雪峰

    您好,一直想请教,我在clr via c#书中查到的是Type object pointer ,而您一直提到MethodTable Ptr。为什么会有不同的说法?这两个有什么异同呢?有没有工具或代码对指针可以验证呢?请指教。谢谢!
    2009年10月7日 6:53
  • 你好!
         你看的是这本书的第二版了,第二版里的确在图中把MethodTablePtr换成了Type object Ptr了!
         但是MethodTablePtr和TypeObjectPtr在某种意义上是一样的,这个字段的最终目的是想说明对象的类型,而 CLR内部区分类型是根据MethodTable来区分的,因为每一个类型都定义了自己的MethodTable。
         这里Jeffery这样写是把这个字段的真正意图告诉读者,以免引起读者的误解,实际上这个字段保存的是方法表指针!
         我也是请教了一个朋友才了解这些底层机制的,很高兴和你讨论这个问题!
        
    周雪峰
    2009年10月7日 12:29
    版主
  • 您好,谢谢您的回复。
    我这里刚好也有第一版本的图书,查阅了一下那个含有MethodTablePtr的图,可惜没有看到进一步的说明。
    我个人基本上认同是方法表指针的观点,但似乎还缺少了什么。根据对.Net本质论的进一步查阅。发现应该是指向了一个名为Corinfo_Class_Struct结构的表示对象实例类型的数据结构。这个表示类型的数据结构除了是一个方法表以外,还应包含指向基类或接口的指针。或许Jeffery觉得不妥所以改成了TypeObjectPtr。
    2009年10月7日 14:32
  • 不客气啊!
    实际上,我感觉标记成TypeObjectPtr反而容易让人产生误解!更加底层的知识还是需要参考.net本质论的,或是直接参考SSCLI的代码。
    我感觉伟大的Jeffery讲述的深度刚刚合适,太深入底层容易和实践分离的!除非我们需要参加某个CLI虚拟机的项目开发!

    周雪峰
    2009年10月8日 5:08
    版主
  • 哈,我的观点则不同,首先说明是TypeObjectPtr还是MethodTablePtr,并不复杂。即使没有参考CLI的虚拟机实现也很好理解。
    如果Jeffery觉得没问题,他应该不会改,毕竟MethodTablePtr是第一版本的书。
    2009年10月8日 5:22
  • 这两个成员都是只在图中标记出来的,没有其他的说明了!
    当然,应该是Jeffery后来认为TypeObjectPtr更准确!
    周雪峰
    2009年10月8日 10:25
    版主
  • 我了解到好像有两个指针
    一个是SyncIndexBlock,是管理多线程引用的
    一个是TypeHandle,指向MethodTable的.
    这是从<必须知道的.Net>里看到的.
    不知道雪峰版主和Jiyuan兄提到的TypeObjectPtr和MethodTablePtr是什么呢?是否和我说的TypeHandle是一个东西?
    看了各位的留言才知道原来是用TypeObjectPtr这个东西管理类型的.
    那么值类型的方法表,多线程引用和类型管理用的是什么呢?
    最近刚刚开始看Applied Microsoft .net Framework Programming这本书..
    暂时还有太多不懂的,呵呵..

    在new的时候依我理解,在引用类型时会创建4byte的SyncBlockIndex和4byte的TypeHandle和类型中的字段信息,以4byte为单位.
    好像在没有引用的时候SyncBlockIndex的值为-1. TypeHandle指向类型所对应的方法表.一个类型的方法表只创建一次.
    方法表中的方法按顺序排列,父类型的方法在前,子类型方法在后.这样当调用时在父类中找到相应的方法签名时,如果该方法为virtual,则在子类型中查找,如果有重写,则调用子类中的方法.

    - -
    不知道理解的对不对..
    今年年初刚刚开始做DotNet..还有太多不懂不理解的..还要向大家请教..呵呵..
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月9日 3:05
  • 你好!
         实际上就是你提到的TypeHandle,是一个意思,这个是指向方法表的,我们伟大的Jeffery在第二版中把MethodTablePtr更改成了TypeObjectPtr了!
    周雪峰
    2009年10月9日 4:53
    版主
  • 那具体的MethodTablePtr和TypeObjectPtr的区别是什么呢?
    为什么TypeObjectPtr更精确呢?
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月9日 4:55
  • 你提到的值类型的多线程问题不存在,因为值类型是保存在“栈”中的,也叫线程栈,一个线程对应一个栈!

    周雪峰
    2009年10月9日 5:26
    版主
  • 恩,的确是有一句说值类型的内存分配在线程的堆栈上,那么方法呢?
    如果没有类似TypeObjectPtr的东西,那么方法如何调用呢?但,如果加了TypeObjectPtr,会增加栈上的内存负担.
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月9日 5:29
  • 值类型没有TypeObjectPtr这个附加字段!
    周雪峰
    2009年10月9日 5:42
    版主
  • 关于方法调用,我写了如下Demo:
           static void Main(string[] args)
            {
                A a = new A();
                a.Add();

            }
            public struct A
            {
                public int x;
                public int y;

                public int Add()
                {
                    return x + y;
                }
            }
    IL代码如下:
      .locals init ([0] valuetype ConsoleApplication1.Program/A a)
      IL_0000:  nop
      IL_0001:  ldloca.s   a
      IL_0003:  initobj    ConsoleApplication1.Program/A
      IL_0009:  ldloca.s   a
      IL_000b:  call       instance int32 ConsoleApplication1.Program/A::Add()
      IL_0010:  pop
      IL_0011:  ret

    注意这行IL_0001:  ldloca.s   a,这里载入的是a的地址,在这个地址上调用的方法!
    周雪峰
    2009年10月9日 6:47
    版主
  • "在这个地址上调用的方法"这句我还不是很理解...
    慢慢看吧...
    恩,我知道值类型没有TypeObjectPtr这个字段,在值类型装箱时才会在堆区添加TypeObjectPtr和SyncBlockIndex.
    3Q雪峰版主...
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月9日 7:17
  • 你好!
         深入理解值类型和引用类型,这是.NET开发人员取得长期成功的关键,下面从内存布局角度详细给大家说明一下值类型和引用类型:
         值类型的内存结构:

         引用类型的内存结构:

         
    引用类型的实例比值类型的实例多了两个附加的字段,SyncBlockIndex和RTTI(运行时类型信息)指针,指向一个方法表结构,所以描述为MethodTablePtr和TypeObjectPtr都是可以的,〈CLR Via C#〉这本书的第一版标注的是MethodTablePtr,第二版标注的是TypeObjectPtr,这里应该是Jeffery认为TypeObjectPtr更加准确。
    另外,除Object和ValueType类型以外,值类型不能继承其他任何类型,同时也无法作为其他值类型的基类,所以一般情况下,也就无需方法表指针了!需要调用调用从Object或ValueType继承的一些方法的时候,需要一个方法表指针,这时就需要对值类型进行装箱!
    看一个例子:
    public struct A
    {
         public overrid String ToString()
         {
                 return "A++";
          }
    }

    static void Main(string[] args)
    {
           A a=new A();
           a.ToString();//这时不会发生装箱,因为C#编译器发现结构体A重写了ToString方法,于是产生直接调用ToString的IL指令,而且不可能有类型继承自结构体A,所以编译器可以确定这里不会有多态性的行为,于是就无须方法表指针,也无须装箱!
           a.GetType();//这里会发生装箱,因为结构体A本身没有实现GetType方法,需要调用继承的GetType方法,这时需要一个方法表指针来访问方法表,这时需要通过装箱来获得!


    周雪峰
    2009年10月9日 10:19
    版主
  • 雪峰版主,这图还是有些不明白..
    0x50-0x5C的地方存储的是什么?
    0x5C的地方指向AppDomain wide Interface Offset Table中的地址和当前的类型有什么关系?
    从0x5C到AppDomain wide Interface Offset Table,再到0x70和0x78.这里的设计关系不是很清晰的明白..
    比如.代码:
    interface Flyable
    {
        void Fly();
    }

    class Bird : Flyable
    {
        public void Fly(){ }

        static void main()
        {
            new Bird().Fly();
        }
    }

    在调用从接口实现的Fly方法的时候,是如何从方法表中查找的呢?
    不是很清晰这里的设计关系..为什么要有0x5C和AppDomain wide Interface Offset Table这两块内存.
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月10日 2:50
  • 你好!
         0x50-0x5C存储的是必要的表头信息!主要是一些描述这个方法表的一些信息!
         你可能对接口映射表存在的意义有一定的疑问!
         这个表存在的意义就是加快接口方法地址的查找速度!
         当你调用接口方法的时候,先是获取接口映射表的地址,然后在接口映射表查找到本接口在方法表中的地址,然后在方法表的这个接口的方法地址列表中查找这个方法的地址,然后通过方法地址找到函数定义
        
    周雪峰
    2009年10月10日 4:15
    版主
  • To 韩懿留-獨翏淚:
       
        抱歉才看到,发现已讨论的比较深入了。其实周版主已解释的很清楚了。
        我来说说个人对AppDomain wide Interface Offset Table的理解。AppDomain wide Interface Offset Table其实说明了当前类型实现了某个接口,是对该接口的指向。而0x70和0x78是当前类型对接口的具体实现,其实答案您自己已经说出来了。类似这段话“这样当调用时在父类中找到相应的方法签名时,如果该方法为virtual,则在子类型中查找,如果有重写,则调用子类中的方法.”。
    2009年10月10日 5:03
  • 你好!
         0x50-0x5C存储的是必要的表头信息!主要是一些描述这个方法表的一些信息!
         你可能对接口映射表存在的意义有一定的疑问!
         这个表存在的意义就是加快接口方法地址的查找速度!
         当你调用接口方法的时候,先是获取接口映射表的地址,然后在接口映射表查找到本接口在方法表中的地址,然后在方法表的这个接口的方法地址列表中查找这个方法的地址,然后通过方法地址找到函数定义
        
    周雪峰

    "当你调用接口方法的时候,先是获取接口映射表的地址,然后在接口映射表查找到本接口在方法表中的地址,然后在方法表的这个接口的方法地址列表中查找这个方法的地址,然后通过方法地址找到函数定义"
    感谢两位的回答...
    不过看到雪峰版主这句,表明了当调用时,就已预先知道了该方法是接口中的方法..
    我觉得在调用一个引用类型中的一个方法时,会用方法签名从前向后查找签名匹配的方法,而不需要获取接口映射表的地址.
    这样:
    按照如上的说法,在查找方法时,应分情况调用:1,接口方法,调用的步骤如雪峰版主所说.先获取接口映射表地址,然后在RTTI中找到该方法的实现.2,非接口方法,不需要先获取接口映射表地址.直接从前向后查找匹配的方法签名.
    事实如此吗?这些东西搞来搞去的总是让人有些费解,但是一但明白了,就好了..
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月10日 5:36
  • 你好!
         调用的时候CLR是知道这个方法是接口方法的!
         大家注意RTTI,这是运行时类型信息,而不是编译时类型信息!
         比如:MyClass c=new MyClass();
                  Object o=c;
                  o.GetType();//这里使用的是c的方法表!
    周雪峰
    2009年10月10日 11:33
    版主
  • 非常感谢雪峰老大一次次的说教..
    呵呵....
    结帖吧.
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年10月12日 13:23
  • 不客气啊!
    我也在研究这个方面的问题,有了新的结论再分享给大家!
    周雪峰
    2009年10月13日 5:38
    版主