17

CLRコンパイラ/JITによって実行されるエスケープ分析はありますか?たとえば、Javaでは、ループをエスケープしないループに割り当てられたオブジェクトのループ変数が、ヒープではなくスタックに割り当てられるように見えます( Javaでのエスケープ分析を参照)。

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());
}
4

3 に答える 3

15

オブジェクト( )を意味する場合new Foo(i);、私の理解ではありません。これはスタックに割り当てられることはありません。ただし、第0世代で消滅するため、収集は非常に効率的です。私はCLIの暗くて暗い隅をすべて知っているとは言いませんが、管理された参照型がスタックに割り当てられることにつながるC#のシナリオをstackalloc認識していません(実際には数えられない、非常に具体的です)。明らかに、C ++にはさらにいくつかのオプションがありますが、それはマネージドインスタンスではありません。

興味深いことに、MonoTouch / AOTではすぐに収集される可能性がありますが、これはメインのCLI VMではありません(非常に特殊なシナリオ用です)。

変数について、通常はスタック上にあります(そして、ループの反復ごとに再利用されます)が、そうではない場合があります。たとえば、これが「イテレータブロック」である場合、削除されていないすべてのローカル変数は、実際にはコンパイラによって生成されたステートマシンのフィールドです。より一般的には、変数が「キャプチャ」される場合(匿名メソッドまたはラムダ式に変換され、どちらもクロージャを形成します)、変数はコンパイラによって生成されたキャプチャコンテキストのフィールドに変換され、ループの反復ごとに分離されますfooループ内で宣言されているため)。これは、それぞれがヒープ上で分離していることを意味します。

i(ループ変数)に関しては-それキャプチャされると、さらに興味深いものになります

  • C#1.2ではキャプチャは存在しませんでしたが、仕様によれば、ループ変数は技術的には反復ごとです。
  • C#2.0から4.0では、ループ変数が共有されます(悪名高いキャプチャ/ foreachの一般的な質問が発生します)
  • C#5.0以降では、ループ変数は再び反復されます

これは、変数がキャプチャされたときにのみ違いをもたらしますが、キャプチャコンテキストでの変数の表示方法のセマンティクスを正確に変更します

于 2011-11-22T07:43:20.750 に答える
6

値型はスタックに割り当てられる場合がありますが(常にではありません)、参照型のインスタンスには同じことが当てはまりません。実際には:

特に、参照型のインスタンスの保存場所は、たとえ短命であることが証明されたとしても、常に長命であるかのように扱われます。したがって、それらは常にヒープに移動します。

(Eric Lippert:値型についての真実

また、スタックは実装の詳細であり、読みやすくなっています。

于 2011-11-22T07:26:30.787 に答える
1

x86 JITは値型の「インライン化」に優れていますが、ToStringメソッドはボックス化されたオブジェクトに対する仮想呼び出しであるため、スニペットは修飾されません。編集:オーバーライドしていないため、これは当てはまらない場合がありますToString

ただし、x64 JITは、私の実験ではこれをまったく行いません。

編集:

可能であれば、x86とx64の両方でコードをテストしてください。

于 2011-11-22T07:03:03.023 に答える