.NET CLR对空类型中的内存开销有何作用?

时间:2014-11-15 23:39:26

标签: .net clr

所有.NET类都将同步块和类型指针存储为其实例的一部分。这些在32位进程中总共占用8个字节,在64位进程中占用16个字节。但是,空类型实例的对象大小分别为12和24个字节。

我看过一些文章说这是一个对齐问题,但由于同步块和类型句柄是指针大小的,我不明白为什么需要添加任何填充。

其他文章说垃圾收集器需要它,但它对开销有什么影响?它不能存储任何内容,因为如果类型具有实例字段,则实际使用额外的空间。垃圾收集器在对象最终化之后和释放之前是否需要放置某些东西(可能是指针)之前对该内存执行某些操作?

以下是我读过的有关空类型尺寸的一些文章:

Performance Considerations of Class Design and General Coding in .NET

  

如果您创建了一个没有字段的对象并在调试器中查看它,您会注意到它的大小实际上是12个字节,而不是8.对于64位进程,该对象将是24个字节。这是因为最小尺寸基于对齐。值得庆幸的是,这个“额外的”4字节空间将由字段使用。

Of memory and strings

  

分别有12个字节和24个字节的“最小”大小。换句话说,你不能拥有只是开销的类型。注意“Empty”类如何占用与创建Object实例相同的大小...实际上有一些空余空间,因为CLR不喜欢在没有数据的对象上操作。

Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

  

如前所述,当前的GC实现需要一个至少12个字节的对象实例。如果一个类没有定义任何实例字段,它将带有4个字节的开销。 8个字节的其余部分将被Object Header(可能包含syncblk编号)和TypeHandle占用。

2 个答案:

答案 0 :(得分:3)

是的,即使是一个空类,对于通常存储对象字段的对象部分也需要4/8字节。所以一个完全空的类在32位模式下仍然需要4 + 4 + 4 = 12个字节,在64位模式下8 + 8 + 8 = 24个字节。当对象存在时,不会使用那些4/8额外字节。

释放对象时需要此存储空间。然后它可以成为堆段的空闲块列表的一部分。如果堆段包含固定对象并且无法完美压缩,则会发生这种情况。在这种情况下,同步块在调试版本中设置为-1,类型句柄设置为内部伪FreeObject类型。对象大小需要4个字节。

可见SSCLI20 source code,clr / src / vm / gcsmp.cpp文件,SetFree()函数。

答案 1 :(得分:2)

啊,我知道发生了什么。

Arrays将它们的长度存储为其实例字段数据的前4个或8个字节(分别在32位和64位系统中)(在类型指针之后)。为了获得内存使用的大小,CLR从方法表(由类型指针指向)获取基本大小,并添加长度乘以每个项目大小(它也从方法表中获取)。

换句话说,公式是:

  

内存大小=基本大小+长度*项目大小

CLR的实现者不是拥有一个数组公式而另一个是其他类型的公式,而是希望两者都有一个公式,从而在获取内存大小时无需任何条件逻辑。

但那怎么可行呢?其他对象类型不会在其实例字段数据的前4或8个字节中存储长度。

键是项目大小,存储在方法表中。对于非数组类型,项目大小为0.这意味着无论实例字段数据的前4或8个字节中存储的值如何,长度*项目大小始终为0,并且公式将起作用。

但即使实例字段数据的前4或8个字节的值不重要,仍然需要分配它以防止访问冲突。

感谢Hans指出我的SetFree方法。一旦我看到CLR本质上是将一个任意对象转换为一维字节数组,我意识到它假设所有东西都可以以这种方式转换,并且调查为什么导致我得到这个答案。