一个天真的类型系统会将对象存储为指向其类型的指针(其中包含许多有用的信息,如vtable,对象大小等),后跟其数据。如果.Net有这样一个类型系统,object
将在32位系统上占用4个字节,在64位上占用8个字节。
We can see that it doesn't。对象开销是两个指针大小,另外,还有一个指针大小的“最小”大小。
那么object
实际存储在幕后的是什么?
答案 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!
};
请注意这一部分:
同步块索引实际上是实例的 负偏移量 。
所以同步块实际上并没有跟随方法表(正如汉斯提到的那样),但它之前 - 所以它不是“正常”对象的一部分(缺少更好的词)。