在 64 位处理器上为 3 Point 结构分配了多少字节?
Posted
技术标签:
【中文标题】在 64 位处理器上为 3 Point 结构分配了多少字节?【英文标题】:How many bytes get allocated for 3 Point structs on a 64-bit processor? 【发布时间】:2017-01-05 10:26:18 【问题描述】:有个问题:
给定:
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 个字节 是什么?
【问题讨论】:
你的意思是 2 * 8 对吧? 您在询问 .NET 的内部结构,这可能很难回答,因为它们是……嗯……内部结构。 Jon Skeet 做了一些研究并得出结论,值类型数组的开销为 12 个字节:***.com/a/1589806/98607 谁说p
在堆栈中/在堆栈中?
它可以被 lambda 或异步方法的一部分捕获(关闭)。
我不知道 .net 的深层工作原理,但这实际上是用语言定义的吗?它似乎相当有限,它将定义它可以为对象分配的确切堆空间量。
【参考方案1】:
您对 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 字节 因此整个数组的长度为 48 字节,并且变量指向数组:56 字节强>。
64位架构内存布局:
注意事项:
如果您的数组没有四舍五入到 8 字节,则会发生多次对齐,但不需要对齐。示例 (两个 1 大小的 int
数组):
尽管标头的长度字段是 64 位的 8 字节,但它大于 .NET
允许的最大数组大小,因此只能使用 4。 /em>
请记住,这是一个实现细节,它可能会在 CLR 的实现/版本之间发生变化。
【讨论】:
所以即使在 64 位拱门上,您也仅限于 2^32 个数组成员? @Paladin 索引器也仅限于int
,所以它的int.MaxValue
是2^31
。
@TamirVered:大文件的内容?科学/数值计算,其中数组是这种情况的明显类型? ...
@Paladin 可能是为了避免与涉及有符号和无符号值的数学相关的大量陷阱。 (通常,.net 尽可能避免使用无符号值。)IIRC C# 在这些情况下需要大量额外的强制转换(理论上)让开发人员承认可能会出错。
@R.. 在加载文件时拆分文件...到行/反序列化数据/分组数据。在进行数值/科学计算之前,将数据安排在一些更复杂的结构中。我同意在某些情况下人们会想要这样做,但可能会有更好的选择。【参考方案2】:
其中大部分纯粹是实现细节,可能会随着 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/doubles/pointers 时获得良好性能所必需的(替代方法是只为这些类型添加填充并专门化长度计算 - 不太值得额外的复杂性)。
在 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
DATA_ALIGNMENT
对于 x86 (vm/i386/cgencpu.h) 和 ARM (vm/arm/cgencpu.h) 定义为 4,对于 x64 (vm/amd64/cgencpu.h) 定义为 8。假设数据对齐是 2 的幂次方方法,代码本身不过是标准优化的“四舍五入到 DATA_ALIGNMENT
的下一个倍数”。
【讨论】:
@hatchet 我还添加了一些对支持该声明的 coreclr 源代码的引用。以前从未看过,但我很惊讶(我猜可能不应该有这么多合理的 VM 层次结构)结构与 HotSpot 有多么相似 - 很容易找到你的方式。 由于它是一个实现细节,因此需要注意这个答案完全不适用于 Mono,我在 x64 上获得 56 个字节。 @cat 包括指向数组的变量?,这也是您在 .NET 中应该得到的...阅读我的答案。 很好的答案,但你应该链接到 Jon Skeet 的帖子,你从那里偷了代码示例,不管是无耻的还是其他的。 @Voo Skeet 的源代码在这里codeblog.jonskeet.uk/2011/04/05/of-memory-and-strings【参考方案3】:说到x86
架构,44 bytes的答案是不正确的,因为x86
中的对象引用大小是4字节,而不是8字节,因此对象长度为36字节 + 4 个字节 对对象的引用给出 40 个字节。如果我错了,请纠正我。
【讨论】:
答案 44 字节适用于 64 位架构。请看问题 @JohnSmith 我指的是 Tamir Vered 的回答,指出 44 字节对于 32 位是正确的,我认为这不是 我的错,没有从你的评论中得到它。 @John 考虑到 x64 的大小是 48 字节,而不考虑指向不太可能的结构的指针。以上是关于在 64 位处理器上为 3 Point 结构分配了多少字节?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Windows 7 64 位机器上为 anaconda 的 python 3.5 安装 cvxopt?
在 32 位机器上为 64 位编译一个 c# 项目? (视觉工作室 2015)
尝试在 OS X 上为 32 位和 64 位编译 GNU 库
如何在 Windows64 上为 Python 3.5 安装 pydotplus
在 Aleph One 上工作时,在 64 位系统上没有堆栈分配
为啥我在 IA32_LSTAR MSR 上使用内核调试器在 64 位 Windows 上为 SYSCALL 设置断点时会出现 DoubleFault? (KVASCODE 部分)