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

时间:2016-08-29 11:38:32

标签: 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(Point) 数组堆中的 3 * 8 = 24字节

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

3 个答案:

答案 0 :(得分:41)

您对 44字节 的回答可能是对32位架构数组的混淆。

.Net (32位)

  • 每个object包含 4个字节 以进行同步(lock (obj))。
  • 每个object包含其类型标记的 4个字节
  • 每个array包含 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 };

将是:

Memory layout

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

在64位中,标题的每个字段都带有 8字节 ,因此标题长度 24字节 < / strong>因此整个数组的长度为 48字节 ,并且变量指向数组: 56字节

64位架构内存布局:

64 bit memory layout

<强> 注意:

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

    Alignment

  • 即使标头的长度字段在64位中 8字节,它仍然大于最大数组.NET允许,因此只能使用4

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

答案 1 :(得分:13)

大部分内容纯粹是一个实现细节,可能会随着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);

    }
}

代码很简单,但是shamelessly stolen from Jon Skeet

如果您运行此操作,您将获得以下结果:

x86: 36 byte
x64: 48 byte

在当前实现中,每个对象的大小与指针大小对齐,这意味着x86中的每个对象都是4字节对齐的,并且在x64 8字节下(这绝对可以改变 - 例如Java中的HotSpot对齐所有内容即使在x86下也是8字节。

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

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

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

对于任何想要实际证据的人而不是关于标题大小和对齐的经验测试,您只需转到实际来源:Here是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),

DATA_ALIGNMENT定义为4,对于x64(vm / amd64 / cgencpu.h)定义为8。代码本身只是标准优化的“舍入到DATA_ALIGNMENT的下一个倍数”,假设数据对齐是2方法的幂。

答案 2 :(得分:1)

说到x86架构, 44字节 的答案不正确,因为x86中的对象引用大小为 4个字节 ,而不是 8个字节 ,因此 36个字节的对象长度 < / strong> + 4字节 对象的引用提供 40字节 。如果我错了,请纠正我。