我正在优化内存消耗应用程序。在此背景下,我有一个关于C#引用类型大小开销的问题。
C#对象所占用的字节数等于其字段占用的字节数加上一些额外的管理开销。我推测这个管理开销在不同的.NET版本和实现中可能会有所不同。
您知道C#对象的管理开销大小是多少(如果overhead是可变的,则是最大值)(C# 4.0和Windows 7,8环境)吗?
管理开销是否因32位或64位.NET运行时而异?
我正在优化内存消耗应用程序。在此背景下,我有一个关于C#引用类型大小开销的问题。
C#对象所占用的字节数等于其字段占用的字节数加上一些额外的管理开销。我推测这个管理开销在不同的.NET版本和实现中可能会有所不同。
您知道C#对象的管理开销大小是多少(如果overhead是可变的,则是最大值)(C# 4.0和Windows 7,8环境)吗?
管理开销是否因32位或64位.NET运行时而异?
通常情况下,由GC分配的每个对象都有8或12字节的开销。在32位运行时中,同步块占用4字节,类型句柄占用4字节,在64位运行时中,这些开销是8字节。详细信息请参见MSDN杂志上的“ObjectInstance”部分的《深入了解.NET Framework内部以查看CLR如何创建运行时对象》。
请注意,实际引用在32位或64位.NET运行时中也会发生变化。
此外,类型的调整填充可能会影响地址边界,不过这取决于所涉及的类型。这也可能导致对象之间出现“空白区域”,但是运行时(主要是通过StructLayoutAttribute)会决定数据对齐的时间和方式。
http://geekswithblogs.net/akraus1/archive/2012/07/25/150301.aspx
您可以通过向数组中添加数百万个对象(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 = (GC.GetTotalMemory(false) - initial - 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位应用程序中为8字节,在64位应用程序中为16字节。
数据成员被填充,使它们从偶数地址边界开始。例如,如果类中有一个byte
和一个int
,则byte
可能会填充三个未使用的字节,以便int
从下一个机器字边界开始。
类的布局由JIT编译器根据系统架构确定(并且可能在框架版本之间有所不同),因此对C#编译器来说是未知的。