2011-11-22 48 views
14

CLR編譯器/ JIT執行任何轉義分析嗎?例如,在Java中,似乎 循環變量 在循環中分配的對象不會轉移循環,而是分配到堆棧而不是堆(請參閱Escape analysis in Java)。.NET CLR虛擬機中的轉義分析

爲了闡明,在下面的例子中,編譯器會優化掉foo的堆分配,因爲它永遠不會退出循環。

class Foo 
{ 
    int number; 
    Foo(int number) { this.number = number; } 
    public override string ToString() { return number.ToString(); } 
} 

for (int i = 0; i < 10000000; i++) 
{ 
    Foo foo = new Foo(i); 
    Console.WriteLine(foo.ToString()); 
} 
+0

典型地,語義,循環變量是值類型,其被在所分配的堆棧(在循環的情況下)。 –

+0

我的意思是在循環體內分配一個變量。我會更新這個問題來澄清。 – SimonC

+0

你的意思是*變量*?或*對象*? (概念上非常不同) –

回答

11

如果你的意思是對象new Foo(i);),那麼我的理解是,否:這是從來沒有分配在堆棧上;然而,它會在第零代死亡,因此收集效率非常高。我不知道CLI的每一個陰暗和陰暗的角落,但我沒有意識到任何場景在C#這將導致管理參考類型被分配在堆棧上(像stackalloc事情並不真的計數,並且非常具體)。很顯然,在C++中你有更多的選擇,但它不是一個託管實例。

有趣的是,在MonoTouch/AOT上,它可以立即收集,但這不是主要的CLI VM(並且用於特定的場景)。

至於可變 - 即通常是在堆棧上(和重新用於每次循環迭代) - 但它可能不是。例如,如果這是一個「迭代器塊」,那麼所有未刪除的局部變量實際上都是編譯器生成的狀態機上的字段。更常見的是,如果變量被「捕獲」(變爲匿名方法或lambda表達式,它們都形成閉包),那麼變量將被轉換爲編譯器生成的捕獲上下文中的字段,在每個循環中是分開的迭代(因爲foo在循環中聲明)。這意味着每個都在堆上分開

至於i(循環變量) - 如果被捕獲的,它會在C#1.2捕獲不存在更有趣

  • ,但在規範的循環變量是技術上每次迭代
  • 在C#2.0至4.0,循環變量是共享的(導致臭名昭著捕獲/的foreach常見問題)
  • 在C#5.0以上循環變量是每個迭代再次

這不僅使當變量被捕獲的差,但改變的確切它如何體現在捕獲上下文

1

而x86 JIT善於「內聯」值類型,您的片段將沒有資格作爲ToString方法將是一個盒裝的對象上的虛擬呼叫。 編輯:這可能不是這種情況,因爲您不是覆蓋ToString

然而,x64 JIT根本不會從我的實驗中完成這項工作。

編輯:

如果可能的話,測試在x86和x64的代碼。

+1

'Foo'雖然是一個類,所以我不知道這適用多少。 –

5

值類型可能在堆棧上分配(並非總是),但對於引用類型的實例也是如此。事實上:

特別是,引用類型實例的存儲位置總是被視爲長期存在,即使它們可證明是短暫的。所以他們總是堆在一堆。

(埃裏克利珀:The Truth About Value Types

而且The Stack Is An Implementation Detail使一個良好的閱讀。