【问题标题】:Where CLR allocates local memory pool?CLR 在哪里分配本地内存池?
【发布时间】:2024-05-14 21:15:04
【问题描述】:

ECMA-335,I.12.3.2.4 声明如下:

每个方法状态的一部分是一个本地内存池。可以使用localloc 指令从本地内存池中显式分配内存。本地内存池中的所有内存都在方法退出时回收,这是回收本地内存池内存的唯一方式(没有提供释放在此方法调用期间分配的本地内存的指令)。本地内存池用于分配类型或大小在编译时未知且程序员不希望在托管堆中分配的对象。由于本地内存池在方法的生命周期内无法收缩,因此语言实现不能使用本地内存池进行通用内存分配。

CLR 在哪里分配这个内存池?是托管堆、线程栈等吗?

【问题讨论】:

  • 我不能以任何权威说,但这对我来说听起来像是堆栈分配。但是,我怀疑该标准并未明确要求为实施者提供更大的灵活性。

标签: c# .net memory-management clr


【解决方案1】:

这都是故意模糊的,因为它是 CLI 规范不想确定的强大实现细节。它通过Opcodes.Localloc 的 MSDN 文章中的裂缝窥视:

如果没有足够的内存来处理请求,则会引发 *Exception。

获得 SOE 只有一种方式:它需要从堆栈中分配。

C# 语言在分配位置方面不太害羞,它使用 stackalloc 关键字。一个示例程序:

class Program {
    static unsafe void Main(string[] args) {
        int* p = stackalloc int[42];
    }
}

产生这个 IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  ldc.i4.s   42
  IL_0002:  conv.u
  IL_0003:  ldc.i4.4
  IL_0004:  mul.ovf.un
  IL_0005:  localloc                   // <=== Here
  IL_0007:  pop
  IL_0008:  ret
} // end of method Program::Main

在运行时产生这个机器代码:

02E42620  push        ebp  
02E42621  mov         ebp,esp  
02E42623  sub         esp,8  
02E42626  mov         dword ptr [ebp-4],esp  
02E42629  mov         dword ptr [ebp-8],6A029823h  
02E42630  mov         eax,esp  
02E42632  test        dword ptr [esp],esp  
02E42635  sub         eax,0A8h                       // <=== Here
02E4263A  mov         esp,eax  
02E4263C  mov         dword ptr [ebp-4],esp  
02E4263F  cmp         dword ptr [ebp-8],6A029823h  
02E42646  je          02E4264D  
02E42648  call        730CA5C0  
02E4264D  lea         esp,[ebp]  
02E42650  pop         ebp  
02E42651  ret  

sub eax,0A8h 指令从 ESP 寄存器(堆栈指针)中减去 0xa8 = 168 = 42x4 个字节,mov esp,eax 指令调整堆栈指针。所以是的,这肯定来自堆栈。

【讨论】: