.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 上的内存注意事项应侧重于对象创建和生命周期管理,而不是值类型。
我试图找到可在 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 上的内存注意事项应侧重于对象创建和生命周期管理,而不是值类型。