2009-12-15 70 views
1

我不是任何類型的IL大師,我只是有時用它來檢查編譯器對我編寫的代碼所做的事情。我一直在想的一件事是爲什麼.maxstack有時會獲得它所獲得的價值。考慮下面的類:不存儲參考時放大.maxstack?

public class Sample 
{ 
    public void SomeMethod(){} 
} 

然後,我有一個這樣的程序:

private static void Main(string[] args) 
{ 
    Sample sample = new Sample(); 
    sample.SomeMethod();  
} 

上面的代碼提供了以下IL(發佈編譯):

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  13 (0xd) 
    .maxstack 1 
    .locals init ([0] class ConsoleApplication1.Sample sample) 
    IL_0000: newobj  instance void ConsoleApplication1.Sample::.ctor() 
    IL_0005: stloc.0 
    IL_0006: ldloc.0 
    IL_0007: callvirt instance void ConsoleApplication1.Sample::SomeMethod() 
    IL_000c: ret 
} // end of method Program::Main 

現在,如果我將程序代碼更改爲:

private static void Main(string[] args) 
{ 
    new Sample().SomeMethod(); 
} 

...它導致以下IL代碼:

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  11 (0xb) 
    .maxstack 8 
    IL_0000: newobj  instance void ConsoleApplication1.Sample::.ctor() 
    IL_0005: call  instance void ConsoleApplication1.Sample::SomeMethod() 
    IL_000a: ret 
} // end of method Program::Main 

第二個IL代碼更短,這是預期的。但是,讓我有點好奇的是爲什麼.maxstack我在第二個代碼示例中,但在第一個?爲什麼第二個代碼會導致系統爲操作預留更大的堆棧?

+0

這是處於調試還是發佈模式?我不知道stdloc.0和ldloc.0的必要性是什麼,除了使調試更容易。 – 2009-12-15 08:58:03

+0

@Cecil:這是釋放complied(正如在問題中所述,但無論如何埋在文本中)。 – 2009-12-15 09:03:06

回答

4

方法頭的二進制表示有一個「小格式」和「胖格式」。這種微小的報頭需要更少的字節,並且可以,只要滿足以下條件,可以使用:

  • 最大堆疊< = 8個
  • 無異常處理
  • 沒有局部變量
  • 代碼大小< 64字節

您的更改允許編譯器使用此表單,並且當遇到小標題時,始終假定使用最大堆棧數8個。

Reference is ECMA-335 §25.4.2

在一個側面說明特別感興趣的我的是,在(在OP按你的註釋)發佈版本,他們產生了不同的代碼,其中縮寫形式是更小並且更快的事實。你使用的是什麼版本的C#?我希望以後的版本能夠利用這種明顯的優化:

  • 可以在不更改語義的情況下刪除局部變量。
  • 當局部變量不存在時,編譯器知道該值的具體類型正好是Sample,因此即使SomeMethod是虛擬的,也可以直接使用call指令對其進行調用。