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

Joh*_*ith 24 .net c# memory arrays struct

有一个问题:

鉴于:

struct Point {int x; int y;}
var p = new Point[3]
Run Code Online (Sandbox Code Playgroud)

如果我们使用64位处理器,将在堆栈和堆中分配多少字节的内存?

正确答案.Net44.有人可以解释这个数字是如何出现的吗?

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

并且我们有Int32每个结构的两个值,因此 堆中的p.Length * sizeof(Point) 3*8 = 24个字节用于数组.

这将是32个字节.在这种情况下剩下的12个字节是多少?

Tam*_*red 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 };
Run Code Online (Sandbox Code Playgroud)

将会:

内存布局

您也可以在内存布局中查看数字值.


在64位中,标题的每个字段占用8个字节,因此标题长度为24个字节,因此整个数组的长度为48个字节,变量指向数组:56个字节.

64位架构内存布局:

64位内存布局


笔记:

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

    对准

  • 即使标题的长度字段是64位中的8个字节,它也比.NET允许的最大数组大小大,因此只能使用4 个字节.

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

  • @Paladin索引器也限于`int`,所以它的`int.MaxValue`是'2 ^ 31`. (4认同)
  • @TamirVered:一个大文件的内容?科学/数值计算,其中数组是明显的类型?... (4认同)
  • @R ..在加载文件时拆分文件...到行/反序列化数据/分组数据.在进行数值/科学计算之前,将数据排列在一些更复杂的结构中.我同意在某些情况下人们会这样做,但可能会有更好的选择. (2认同)

Voo*_*Voo 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);

    }
}
Run Code Online (Sandbox Code Playgroud)

代码非常简单,但却被Jon Skeet无耻地偷走了.

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

x86: 36 byte
x64: 48 byte
Run Code Online (Sandbox Code Playgroud)

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

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

在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. 
Run Code Online (Sandbox Code Playgroud)

您还可以查看实际代码,以查看这些指针是实际指针而不是DWORD或其他任何指针.

对齐对象大小的代码也在同一个文件中:

#define PTRALIGNCONST (DATA_ALIGNMENT-1)

#ifndef PtrAlign
#define PtrAlign(size) \
    ((size + PTRALIGNCONST) & (~PTRALIGNCONST))
#endif //!PtrAlign
Run Code Online (Sandbox Code Playgroud)

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

  • 很好的答案,但你应该链接到Jon Skeet帖子,你从中偷走了代码样本,无耻或其他. (2认同)
  • @Voo Skeet的源代码在这里https://codeblog.jonskeet.uk/2011/04/05/of-memory-and-strings/ (2认同)