C# 在64位处理器上为3点结构分配多少字节?

C# 在64位处理器上为3点结构分配多少字节?,c#,.net,arrays,memory,struct,C#,.net,Arrays,Memory,Struct,有一个问题: 鉴于: struct Point {int x; int y;} var p = new Point[3] 如果我们使用64位处理器,堆栈和堆中会分配多少字节的内存 .Net的正确答案是44。有人能解释一下这个数字是怎么出现的吗 据我所知,p将在x64的堆栈中占用8个字节 每个结构有两个值Int32,因此 p.Length*sizeof(点) 3*8=24字节在一个数组的堆中 这将是32个字节。在这种情况下,剩下的12个字节是干什么的?您对44个字节的回答可能是对32位体系结构数

有一个问题:

鉴于:

struct Point {int x; int y;}
var p = new Point[3]
如果我们使用64位处理器,堆栈和堆中会分配多少字节的内存

.Net
的正确答案是44。有人能解释一下这个数字是怎么出现的吗

据我所知,
p
将在
x64
的堆栈中占用8个字节

每个结构有两个值
Int32
,因此
p.Length*sizeof(点)
3*8=24字节在一个数组的堆中

这将是32个字节。在这种情况下,剩下的12个字节是干什么的?

您对44个字节的回答可能是对32位体系结构数组的混淆

.Net
(32位)中:

  • 每个
    对象
    包含用于同步的4个字节(
    lock(obj)
  • 每个
    对象
    都包含其类型标记的4个字节
  • 每个
    数组
    包含其长度的4个字节
正如您所说,指针是8字节

这与数组本身的24字节一起提供44字节


然而,这是32位的标题布局

如您所见,内存布局包含以下代码:

var p = new Point[3];
p[0] = new Point { x = 1, y = 2 };
p[1] = new Point { x = 3, y = 4 };
p[2] = new Point { x = 5, y = 6 };

var p2 = new Point[3];
p2[0] = new Point { x = 8, y = 8 };
p2[1] = new Point { x = 8, y = 8 };
p2[2] = new Point { x = 8, y = 8 };
将是:

您也可以在内存布局中看到数值


在64位中,标头的每个字段及其长度均为8字节,因此标头长度为24字节,因此整个数组的长度为48字节,变量指向数组:56字节

64位体系结构内存布局:


注意事项:

  • 如果您的数组没有四舍五入到8字节,则会发生多个对齐,但实际上并不需要对齐。示例(两个1大小的
    int
    数组):

  • 即使报头的长度字段是8字节(64位),但它大于最大数组大小
    .NET
    允许的大小,因此只能使用4个字节


请记住,这是一个实施细节,可能会在CLR的实施/版本之间发生变化。

大部分内容纯粹是一个实施细节,可能会随CLR的下一个版本而变化。

以x86或x64运行以下程序,您可以根据经验确定结构的大小:

struct Point { int x; int y; }

class Program
{
    const int Size = 100000;

    private static void Main(string[] args)
    {
        object[] array = new object[Size];
        long initialMemory = GC.GetTotalMemory(true);
        for (int i = 0; i < Size; i++)
        {
            array[i] = new Point[3];
        }
        long finalMemory = GC.GetTotalMemory(true);
        GC.KeepAlive(array);

        long total = finalMemory - initialMemory;
        Console.WriteLine("Size of each element: {0:0.000} bytes",
                          ((double)total) / Size);

    }
}
在当前的实现中,每个对象的大小都与指针大小对齐,这意味着x86中的每个对象都是4字节对齐的,而在x64下是8字节对齐的(这是绝对可能改变的-例如,Java中的HotSpot将所有对象对齐到8字节,即使在x86下也是如此)

C#中的数组在长度上有些特殊:虽然它们有一个4字节的长度字段,但在x64下它们还包括4字节的额外填充(vm/object.h:766包含有趣的部分)。这样做很可能是为了确保实际字段的开头始终在x64下对齐8字节,这是在访问长/双精度/指针时获得良好性能所必需的(另一种方法是仅为这些类型添加填充并专门化长度计算-不太可能值得额外的复杂性)

在x86上,对象头是8字节,数组开销是4字节,这给了我们36字节

在x64上,对象标头为16字节,数组开销为8字节。这给了我们24+24=48字节

对于任何想要实际证明而不是关于头大小和对齐的经验测试的人,您可以直接转到实际的源:是coreclr的对象定义。查看从第178行开始的注释,其中说明:

// The only fields mandated by all objects are
// 
//     * a pointer to the code:MethodTable at offset 0
//     * a poiner to a code:ObjHeader at a negative offset. This is often zero.  It holds information that
//         any addition information that we might need to attach to arbitrary objects. 
您还可以查看实际的代码,看看这些指针是实际的指针,而不是DWORD或其他任何东西

用于对齐对象大小的代码也位于同一文件中:

#define PTRALIGNCONST (DATA_ALIGNMENT-1)

#ifndef PtrAlign
#define PtrAlign(size) \
    ((size + PTRALIGNCONST) & (~PTRALIGNCONST))
#endif //!PtrAlign

数据对齐
对于x86(vm/i386/cgencpu.h)和ARM(vm/ARM/cgencpu.h)定义为4,对于x64(vm/amd64/cgencpu.h)定义为8。代码本身不过是一个标准优化的“数据对齐的下一个整数倍”,假设数据对齐是一种2次幂的方法。

说到
x86
体系结构,44字节的答案是不正确的,因为
x86
中的对象引用大小是4字节,不8字节,因此对象引用的36字节+4字节的对象长度为40字节。如果我错了,请纠正我。

你是说2*8对吗?你是在问.NET的内部结构,这可能很难回答,因为它们是。。。好。。。内部。Jon Skeet做了一些研究,得出结论:值类型数组的开销是12字节:谁说
p
在堆栈上/在堆栈中?它可以被lambda或异步方法的一部分捕获(关闭)。我不知道.net的深层工作原理,但这实际上是在语言中定义的吗?它似乎限制了它可以为对象分配的堆空间的确切数量。因此,即使在64位arch上,您也被限制为2^32个数组成员?@Paladin索引器也被限制为
int
,所以它的
int.MaxValue
2^31
@TamirVered:大文件的内容?科学/数值计算,其中阵列是明显的情况类型@圣骑士可能是为了避免与数学有关的大量有符号和无符号值。(通常.net尽量避免使用无符号值。)IIRC C#在这些情况下需要大量额外的强制转换(理论上)使开发人员认识到
#define PTRALIGNCONST (DATA_ALIGNMENT-1)

#ifndef PtrAlign
#define PtrAlign(size) \
    ((size + PTRALIGNCONST) & (~PTRALIGNCONST))
#endif //!PtrAlign