【问题标题】:.NET CLR Smallest Memory Allocation (32/64-bit).NET CLR 最小内存分配(32/64 位)
【发布时间】:2017-04-30 07:32:53
【问题描述】:

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

【问题讨论】:

  • CLR 不会为每个对象实例分配。相反,它会根据需要分配大段,然后使用这些段来保存对象。

标签: .net garbage-collection clr


【解决方案1】:

如果检查以下代码:

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 上你有四个字。

为了证明这一点,您可以查看内存中的地址空间以及值的堆叠方式:

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

如果我们要使用更小的数字,例如像这样的字节或布尔:

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 字节的块,并尝试将所有对象块放入其中。它使用尽可能多的聪明才智来做到这一点。

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

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

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

【讨论】:

  • 您将这些项目分配为局部变量,因此它们在堆栈上而不是在堆上。恕我直言,在堆上,开销很大。另一方面,分配数组非常有效,因为只有数组会有标题,而不是每个单独的项目。
  • 32 位和 64 位之间应该没有太大区别,因为 .NET 数据类型已经定义了长度,例如Int16 将始终为 16 位。
  • 它们是类的公共字段,从 main 实例化。它们不在堆栈上,它们存在于堆上的对象中。地址显示了这一点,该对象获得了一个 64 字节长的块。正如您所说,x32 和 x64 之间的区别在于,它不是为 64 位数据类型分配两个 dword,而是使用 qword,并使用其他指令来操作它们。
猜你喜欢
  • 2014-05-11
  • 2011-02-28
  • 2010-09-07
  • 2014-09-24
  • 2017-04-27
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多