10

我正在优化内存消耗应用程序。对此,我对 C# 引用类型大小开销有疑问。

C# 对象消耗与其字段一样多的字节,加上一些额外的管理开销。我认为不同 .NET 版本和实现的管理开销可能不同。

您知道C# 对象(C# 4.0 和 Windows 7 和 8 环境)的管理开销的大小(或最大大小,如果开销是可变的)?

32 位或 64 位 .NET 运行时的管理开销是否不同?

4

3 回答 3

16

通常,GC 分配的每个对象有 8 或 12 字节的开销。在 32 位运行时,syncblk 有 4 个字节,类型句柄有 4 个字节,在 64 位运行时有 8 个字节。有关详细信息,请参阅MSDN 杂志上深入了解 .NET 框架内部以了解 CLR 如何创建运行时对象的“ObjectInstance”部分。

请注意,实际引用在 32 位或 64 位 .NET 运行时也会发生变化。

此外,可能存在填充类型以适应地址边界,但这在很大程度上取决于所讨论的类型。这也可能导致对象之间的“空白空间”,但取决于运行时(大多数情况下,尽管您可以使用StructLayoutAttribute影响它)来确定何时以及如何对齐数据。

于 2013-01-11T20:58:04.283 回答
7

网上有一篇标题为“.NET 对象的真相和在 AppDomains 之间共享它们”的文章,其中显示了一些转子源代码和一些实验对象并通过普通指针在应用程序域之间共享它们的结果。

http://geekswithblogs.net/akraus1/archive/2012/07/25/150301.aspx

  • 所有 32 位版本的 CLR 为 12 个字节
  • 所有 64 位版本的 CLR 为 24 字节

通过将数百万个对象 (N) 添加到数组中,您可以很容易地对此进行测试。由于指针大小是已知的,您可以通过将该值除以 N 来计算对象大小。

var initial = GC.GetTotalMemory(true);
const int N = 10 * 1000 * 1000;
var arr = new object[N];
for (int i = 0; i < N; i++)
{
    arr[i] = new object();
}

var ObjSize = (initial - GC.GetTotalMemory(false) - N * IntPtr.Size) / N;

在您的 .NET 平台上获得一个近似值。

对象大小实际上被定义为允许 GC 对最小对象大小做出假设。

\sscli20\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))

例如 32 位,这意味着最小对象大小为 12 个字节,这会留下一个 4 字节的漏洞。对于空对象,这个洞是空的,但是如果你将例如 int 添加到你的空类中,那么它就会被填充并且对象大小保持在 12 个字节。

于 2013-01-11T21:40:32.000 回答
4

对象有两种类型的开销:

  • 用于处理对象的内部数据。
  • 数据成员之间的填充。

内部数据是两个指针,因此在 32 位应用程序中为 8 字节,在 64 位应用程序中为 16 字节。

数据成员被填充,以便它们从偶数地址边界开始。例如,如果您在类中有 abyte和 an ,则可能会用三个未使用的字节填充,以便从下一个机器字边界开始。intbyteint

类的布局由 JIT 编译器确定,具体取决于系统的体系结构(并且可能因框架版本而异),因此 C# 编译器不知道。

于 2013-01-11T20:58:54.887 回答