有一个问题:
假设:
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个字节 是什么?
答案 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 };
将是:
您也可以在内存布局中看到数字值。
在64位中,标题的每个字段都带有 8字节 ,因此标题长度 24字节 < / strong>因此整个数组的长度为 48字节 ,并且变量指向数组: 56字节 强>
64位架构内存布局:
<强> 注意: 强>
请记住,这是一个实现细节,它可能会在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字节 。如果我错了,请纠正我。