2016-09-19 2 views
8

このC#コードをコンパイルしてArrayTest.exeを実行すると、プロセスが数秒間ハングし、1GBのRAMが消費され、StackOverflowExceptionでクラッシュする。どうして?大きなギザギザの配列を初期化すると1GBのRAMが必要になり、StackOverflowExceptionでクラッシュする

public struct Point { } 

public class ArrayTest { 
    public static void Main(string[] args) { 
     Point[][] array = { 
      new Point[]{new Point(), new Point(), /* ... 296 omitted ... */, new Point(), new Point()}, 
      new Point[]{new Point(), new Point(), /* ... 296 omitted ... */, new Point(), new Point()}, 
      /* ... 296 omitted ... */ 
      new Point[]{new Point(), new Point(), /* ... 296 omitted ... */, new Point(), new Point()}, 
      new Point[]{new Point(), new Point(), /* ... 296 omitted ... */, new Point(), new Point()}, 
     }; 
     /* Do nothing and return */ 
    } 
} 

Microsoft(R).NET Framework 4.5用のMicrosoft(R)Visual C#コンパイラバージョン4.0.30319.33440を使用しています。私はコマンドラインでcsc.exeを呼び出し、コンパイルされたEXEを実行しています。この問題は、csc /optimizeフラグを追加すると消えます。上記のスニペットは、実際には私がテストしているコード全体です - 配列が初期化された後、Main()で実行される有用な作業はありません。


問題のコンテキスト:数値テストケースのセットをプログラムにハードコードしようとしました。 〜5(

class Point { int x; int y; } 

Point[][] data = { // About 1000 entries 
    {new Point(1, 2)}, 
    {new Point(5, 3), new Point(0, 6), new Point(1, 8)}, // Different length 
    ... et cetera ... 
}; 
for (Point[] thing : data): 
    test(thing); 

をしかし、私はC#で、このようなコンパイルされたコードにしようとしたとき、配列の初期化は著しく時間を取っ:Javaのは、JavaScript、またはPythonでは、コードは無邪気に次のようになりますと、正常に動作します秒)の間、たとえtest()のfor-loopが実行を開始することができます。

struct Pointにはフィールドが含まれておらず、Main()には配列の初期化だけしか含まれておらず、有効な作業がない上のMVCEに実際のコードが縮小されています。

+3

なぜdownvotes? –

+1

私が与えたタイトルは記述的でなく、潜在的に炎症性ではないと思う。 – Nayuki

+0

@全員:タイトルとタグを適切に編集してください。私は正直に私の問題を分類する方法を知らない – Nayuki

答えて

7

さて、クラスファイルのデバッグ/リリースバージョンのコンパイルを開始しました。 14.0バージョンのツールで見つかったVS 2015コンパイラでは、ILの出力は同じです。これは、人々が問題に気づいていなかった理由をカバーしています。

VS 2013で使用されていた以前のコンパイラでのデバッグとリリースはかなりすぐに間違いです。デバッグモードでの実行可能ファイルの出力は2,091 kbです。リリース版のILは、決して利用されていないので実際のオブジェクトを無視するだけであることを示しています。じゃ、いいよ。 VS 2015 Debug ILとVS 2013 Debug ILを比較します。

簡潔にするために、アレイのサイズを3x3に変更しました。ここ

2015 ILから出力された:

.method public hidebysig static void Main() cil managed 
    { 
    .entrypoint 
    // Code size  45 (0x2d) 
    .maxstack 4 
    .locals init (valuetype Point[][] V_0) 
    IL_0000: nop 
    IL_0001: ldc.i4.4 
    IL_0002: newarr  valuetype Point[] 
    IL_0007: dup 
    IL_0008: ldc.i4.0 
    IL_0009: ldc.i4.3 
    IL_000a: newarr  Point 
    IL_000f: stelem.ref 
    IL_0010: dup 
    IL_0011: ldc.i4.1 
    IL_0012: ldc.i4.3 
    IL_0013: newarr  Point 
    IL_0018: stelem.ref 
    IL_0019: dup 
    IL_001a: ldc.i4.2 
    IL_001b: ldc.i4.3 
    IL_001c: newarr  Point 
    IL_0021: stelem.ref 
    IL_0022: dup 
    IL_0023: ldc.i4.3 
    IL_0024: ldc.i4.3 
    IL_0025: newarr  Point 
    IL_002a: stelem.ref 
    IL_002b: stloc.0 
    IL_002c: ret 
    } // end of method ArrayTest::Main 

このレリーズモードコードとの間の主な違いは、追加のNOP命令です。ここで

は、コンパイラの2012/2013版のため出力されています、あなたが使用している2012/2013コンパイラでは、デバッグモードでは、スタック割り当ての非常に大きな数をので

.method public hidebysig static void Main() cil managed 
    { 
    .entrypoint 
    // Code size  307 (0x133) 
    .maxstack 4 
    .locals init (valuetype Point[][] V_0, 
      valuetype Point[][] V_1, 
      valuetype Point[] V_2, 
      valuetype Point V_3) 
    IL_0000: nop 
    IL_0001: ldc.i4.4 
    IL_0002: newarr  valuetype Point[] 
    IL_0007: stloc.1 
    IL_0008: ldloc.1 
    IL_0009: ldc.i4.0 
    IL_000a: ldc.i4.3 
    IL_000b: newarr  Point 
    IL_0010: stloc.2 
    IL_0011: ldloc.2 
    IL_0012: ldc.i4.0 
    IL_0013: ldelema Point 
    IL_0018: ldloca.s V_3 
    IL_001a: initobj Point 
    IL_0020: ldloc.3 
    IL_0021: stobj  Point 
    IL_0026: ldloc.2 
    IL_0027: ldc.i4.1 
    IL_0028: ldelema Point 
    IL_002d: ldloca.s V_3 
    IL_002f: initobj Point 
    IL_0035: ldloc.3 
    IL_0036: stobj  Point 
    IL_003b: ldloc.2 
    IL_003c: ldc.i4.2 
    IL_003d: ldelema Point 
    IL_0042: ldloca.s V_3 
    IL_0044: initobj Point 
    IL_004a: ldloc.3 
    IL_004b: stobj  Point 
    IL_0050: ldloc.2 
    IL_0051: stelem.ref 
    IL_0052: ldloc.1 
    IL_0053: ldc.i4.1 
    IL_0054: ldc.i4.3 
    IL_0055: newarr  Point 
    IL_005a: stloc.2 
    IL_005b: ldloc.2 
    IL_005c: ldc.i4.0 
    IL_005d: ldelema Point 
    IL_0062: ldloca.s V_3 
    IL_0064: initobj Point 
    IL_006a: ldloc.3 
    IL_006b: stobj  Point 
    IL_0070: ldloc.2 
    IL_0071: ldc.i4.1 
    IL_0072: ldelema Point 
    IL_0077: ldloca.s V_3 
    IL_0079: initobj Point 
    IL_007f: ldloc.3 
    IL_0080: stobj  Point 
    IL_0085: ldloc.2 
    IL_0086: ldc.i4.2 
    IL_0087: ldelema Point 
    IL_008c: ldloca.s V_3 
    IL_008e: initobj Point 
    IL_0094: ldloc.3 
    IL_0095: stobj  Point 
    IL_009a: ldloc.2 
    IL_009b: stelem.ref 
    IL_009c: ldloc.1 
    IL_009d: ldc.i4.2 
    IL_009e: ldc.i4.3 
    IL_009f: newarr  Point 
    IL_00a4: stloc.2 
    IL_00a5: ldloc.2 
    IL_00a6: ldc.i4.0 
    IL_00a7: ldelema Point 
    IL_00ac: ldloca.s V_3 
    IL_00ae: initobj Point 
    IL_00b4: ldloc.3 
    IL_00b5: stobj  Point 
    IL_00ba: ldloc.2 
    IL_00bb: ldc.i4.1 
    IL_00bc: ldelema Point 
    IL_00c1: ldloca.s V_3 
    IL_00c3: initobj Point 
    IL_00c9: ldloc.3 
    IL_00ca: stobj  Point 
    IL_00cf: ldloc.2 
    IL_00d0: ldc.i4.2 
    IL_00d1: ldelema Point 
    IL_00d6: ldloca.s V_3 
    IL_00d8: initobj Point 
    IL_00de: ldloc.3 
    IL_00df: stobj  Point 
    IL_00e4: ldloc.2 
    IL_00e5: stelem.ref 
    IL_00e6: ldloc.1 
    IL_00e7: ldc.i4.3 
    IL_00e8: ldc.i4.3 
    IL_00e9: newarr  Point 
    IL_00ee: stloc.2 
    IL_00ef: ldloc.2 
    IL_00f0: ldc.i4.0 
    IL_00f1: ldelema Point 
    IL_00f6: ldloca.s V_3 
    IL_00f8: initobj Point 
    IL_00fe: ldloc.3 
    IL_00ff: stobj  Point 
    IL_0104: ldloc.2 
    IL_0105: ldc.i4.1 
    IL_0106: ldelema Point 
    IL_010b: ldloca.s V_3 
    IL_010d: initobj Point 
    IL_0113: ldloc.3 
    IL_0114: stobj  Point 
    IL_0119: ldloc.2 
    IL_011a: ldc.i4.2 
    IL_011b: ldelema Point 
    IL_0120: ldloca.s V_3 
    IL_0122: initobj Point 
    IL_0128: ldloc.3 
    IL_0129: stobj  Point 
    IL_012e: ldloc.2 
    IL_012f: stelem.ref 
    IL_0130: ldloc.1 
    IL_0131: stloc.0 
    IL_0132: ret 
    } // end of method ArrayTest::Main 

やっています、編集や継続中にギザギザのアレイ構造全体をインテリセンスにしたり、個々のオブジェクト構造を踏襲したりする可能性があります。私はこれについて全くわからない。

私はILの専門家ではありませんが、それは各ポイントに割り当てられているように見えますが、各アレイに対して再度割り当てられています。

+1

スタック割り当てはどこにありますか? 「評価スタック」は実際の物理スタックではないことに注意してください。 –

+0

私はこれが私の専門分野の外に少しあると言ったように、ちょっと公正で十分です。 .localsが最終的に値型のために格納される場所はスタックにあると私は考えます。あなたが違ったことを知っているなら、私はそれについて聞くのが大好きです。 –

+0

これは当てはまりますが、地元の人は4人しかいません。それらはすべて配列であり、最後の1つのPointインスタンスを除く参照型です。実際の配列の内容は 'newarr'で割り当てられます。これは評価スタックの外側に割り当てられます。*実際には* GCヒープで割り当てられます。 –

関連する問題