.NET CLR 最小内存分配(32/64 位)

.NET CLR Smallest Memory Allocation (32/64-bit)

我试图找到可在 32 位和 64 位系统中通过 CLR 分配的最低内存块的底部。在我看来,在 32 位系统上,它将以 4 字节块分配,而在 64 位系统上,它将以 8 字节块分配。如果为真,Int32 在 64 位系统上是否需要 8 个字节的地址 space?

如果检查以下代码:

public class Numbers
{
    public Int16 A = 1;
    public Int32 B = 2;
    public Int64 C = 3;
    public UInt16 D = 4;
    public UInt32 E = 5;
    public UInt64 F = 6;
    public short G = 7;
    public int H = 8;
}

使用 x64 作为目标平台进行编译和 运行 在反汇编视图中查看时会产生以下指令:

7:         public Int16 A = 1;
mov         rcx,qword ptr [rbp+50h]  
mov         word ptr [rcx+24h],1  

 8:         public Int32 B = 2;
mov         rcx,qword ptr [rbp+50h]  
mov         dword ptr [rcx+18h],2  

 9:         public Int64 C = 3;
mov         ecx,3  
movsxd      rcx,ecx  
mov         rax,qword ptr [rbp+50h]  
mov         qword ptr [rax+8],rcx  

10:         public UInt16 D = 4;
mov         rcx,qword ptr [rbp+50h]  
mov         word ptr [rcx+26h],4  

11:         public UInt32 E = 5;
mov         rcx,qword ptr [rbp+50h]  
mov         dword ptr [rcx+1Ch],5  

12:         public UInt64 F = 6;
mov         ecx,6  
movsxd      rcx,ecx  
mov         rax,qword ptr [rbp+50h]  
mov         qword ptr [rax+10h],rcx  

13:         public short G = 7;
mov         rcx,qword ptr [rbp+50h]  
mov         word ptr [rcx+28h],7  

14:         public int H = 8;
mov         rcx,qword ptr [rbp+50h]  
mov         dword ptr [rcx+20h],8  
mov         rcx,qword ptr [rbp+50h]  

从这里可以看出,它根据数据类型分配了 2、4 和 8 个字节。它不是这样分配处理器字长的块,在 Windows 上一个字总是 16 位,一个双字总是 32 位,而在 x64 上你有四字。

为了证明这一点,您可以查看内存中的地址 space 以及这些值是如何堆叠的:

现在您必须注意,由于所有内存都由 CLR 管理,因此这并不总是可预测的行为。从我的示例中可以看出,它为 Int64 和 UInt64 分配了 8 个字节,为 Int32、UInt32 和 int 分配了 4 个字节,为 UInt16 和 short 分配了 2 个字节。

如果我们使用更小的数字,例如 byte 或 bool,如下所示:

public class Numbers
{
    public byte I = 10;
    public bool J = true;
}

我们得到另一个结果:

 7:         public byte I = 10;
mov         rcx,qword ptr [rbp+50h]  
mov         byte ptr [rcx+8],0Ah  

 8:         public bool J = true;
mov         rcx,qword ptr [rbp+50h]  
mov         byte ptr [rcx+9],1  

现在它使用字节,并且每个只使用 1 个字节。

对象以 64 位块的形式存在于堆中:

然而,CLR 并不总是以这种方式在内存中组织它。 从堆映射中可以看出,CLR 为对象分配了一个 64 字节的块,并试图将所有对象块放入其中。它使用尽可能多的聪明才智来做到这一点。

我尝试了几个不同的版本,分配 space 取决于行为,如果 CLR 分配太少,它将重新分配到其他地方并移动指针,例如前 8 个字节地址 space 用于保存 3,然后它在前 4 个字节中有一些与对象相关的数据,从而与其他数据共享这 8 个字节。它将继续这样做,直到它需要 space 来保存 'C' 的值。它这样做的一种方法是查看它是否可以将 C 向上移动以获得占用的 space。

因此,为了回答您的问题,它将分配的最小块因应用程序以及 CLR 选择为您的程序组织内存的方式而异。您不应该将您的类型视为内存块,认为它们的大小代表了保存它们的 max/min 值所需的最大内存。

一个好的经验法则是认为它分配了类型调用所需的最大值 space,四舍五入到字节,但是由于 CLR 会为您管理所有这些,您实际上不需要担心它。 .NET 上的内存注意事项应侧重于对象创建和生命周期管理,而不是值类型。