.NET幕后:'对象'存储什么?

时间:2011-04-06 03:06:08

标签: .net clr

一个天真的类型系统会将对象存储为指向其类型的指针(其中包含许多有用的信息,如vtable,对象大小等),后跟其数据。如果.Net有这样一个类型系统,object将在32位系统上占用4个字节,在64位上占用8个字节。

We can see that it doesn't。对象开销是两个指针大小,另外,还有一个指针大小的“最小”大小。

那么object实际存储在幕后的是什么?

2 个答案:

答案 0 :(得分:7)

是的,这就是它的样子。 'type handle',又名'方法表指针'在偏移0处,对象数据在偏移量4处跟随。在offset-4处有一个额外的字段,名为'syncblock'。它就在那里,因为它还在未使用对象空间时参与垃圾收集堆,这是一个需要两个指针的双链接空闲块列表。不要让它浪费,syncblock有几个用途,比如存储锁状态,存储哈希码,当需要存储太多时,存储指向显式同步块的指针。

最小的可能对象是盒装字节,4 + 4 + 1 = 9字节。但GC堆的分配粒度是4个字节,因此您将得到4,12个字节的下一个倍数。

使用Visual Studio中的调试器,这一切都非常明显。您可以在this answer中找到提示。

答案 1 :(得分:5)

(全部来自Microsoft Shared Source CLI;它有CLR的源代码。)

如果您查看clr\src\vm\object.h,您会看到:

// The generational GC requires that every object be at least 12 bytes in size.
#define MIN_OBJECT_SIZE     (2*sizeof(BYTE*) + sizeof(ObjHeader))

这是非常不言自明的。此外,在clr\src\vm\gcscan.cpp中,您可以看到诸如

之类的陈述
_ASSERTE(g_pObjectClass->GetBaseSize() == MIN_OBJECT_SIZE);

_ASSERTE(totalSize < totalSize + MIN_OBJECT_SIZE);

我认为这解释了为什么你会看到意想不到的物体尺寸。 :)


<强>更新

@Hans在同步块上有一个很好的观点;我只想指出一个微妙的内容,再次记录在object.h

/* Object
 *
 * This is the underlying base on which objects are built. The MethodTable
 * pointer and the sync block index live here. The sync block index is actually
 * at a negative offset to the instance. See syncblk.h for details.
 */

class Object
{
    protected:
        MethodTable*    m_pMethTab;
    //No other fields shown here!
};

请注意这一部分:

  

同步块索引实际上是实例的 负偏移量

所以同步块实际上并没有跟随方法表(正如汉斯提到的那样),但它之前 - 所以它不是“正常”对象的一部分(缺少更好的词)。