8

私は次のことをしています:

public static class DataHelper
{
  public static List<Seller> Sellers = new List<Seller> {
    {new Seller {Name = "Foo", Location = new LatLng {Lat = 12, Lng = 2}, Address = new Address {Line1 = "blah", City = "cokesville"}, Country = "UK"},
    //plus 3500 more Sellers
  };
}

MVC Webサイト内からDataHelper.Sellersにアクセスすると、StackOverflowExceptionが発生します。Visual Studioでデバッグすると、スタックには5ダースのフレームしかなく、スタックオーバーフローの通常の明らかな兆候はありません(つまり、メソッド呼び出しが繰り返されません)。

アプリの呼び出しは、例外を引き起こすためにこれと同じくらい簡単にすることができます:

public ActionResult GetSellers()
{
  var sellers = DataHelper.Sellers;
  return Content("done");
}

追加情報:

  • 単体テスト内から同じコードを実行すると、問題ありません
  • 売り手の半分(上半分または下半分)を削除すると、Webアプリでは問題ないので、特定の売り手には問題ありません。
  • 私は売り手をプロパティに変更し、最初の呼び出しでリストを初期化しようとしました-助けにはなりません
  • また、半分を1つのリストに追加し、次に半分を別のリストに追加して、2つを組み合わせてみました。

これに対する正解にとても感動します!

4

2 に答える 2

7

これは、事実上、インラインリストの初期化がスタックに対して大きすぎるという事実が原因です。シナリオがほぼ同じであるmsdnフォーラムで、この非常に関連性の高い質問を参照してください。

.Netのメソッドには、スタックの深さとサイズの両方があります。AStackOverflowExceptionは、スタック上の呼び出しの数だけでなく、スタック内の各メソッドのメモリ割り当ての全体的なサイズによっても発生します。この場合、メソッドが大きすぎます。これは、ローカル変数の数が原因です。

例として、次のコードについて考えてみます。

    public class Foo
    {
            public int Bar { get; set;}
    }
    public Foo[] GetInts()
    {
        return new Foo[] { new Foo() { Bar = 1 }, new Foo() { Bar = 2 }, 
          new Foo() { Bar = 3 }, new Foo() { Bar = 4 }, new Foo() { Bar = 5 } };
    }

次に、コンパイル時のそのメソッドのリードインILを確認します(これもリリースビルドです)。

.maxstack 4
.locals init (
    [0] class SomeExample/Foo '<>g__initLocal0',
    [1] class SomeExample/Foo '<>g__initLocal1',
    [2] class SomeExample/Foo '<>g__initLocal2',
    [3] class SomeExample/Foo '<>g__initLocal3',
    [4] class SomeExample/Foo '<>g__initLocal4',
    [5] class SomeExample/Foo[] CS$0$0000
)

注-の前の実際のビット/、つまりSomeExample、メソッドとネストされたクラスが定義されている名前空間とクラスによって異なります-私が書いている進行中のコードからタイプ名を削除するために、数回編集する必要がありました仕事!

なぜそれらすべての地元の人?インライン初期化が実行される方法のため。各オブジェクトは新しくなり、「非表示」ローカルに格納されます。これは、各オブジェクトのインライン初期化でプロパティの割り当てを実行できるようにするためにFoo必要です(オブジェクトインスタンスは、のプロパティセットを生成する必要がありますBar)。 これは、インライン初期化がC#のシンタックスシュガーにすぎないことも示しています。

あなたの場合、スタックを爆破するのはこれらのローカルです(トップレベルのオブジェクトのためだけに少なくとも数千がありますが、ネストされたイニシャライザーもあります)。

C#コンパイラ、代わりに、それぞれに必要な数の参照をスタックにプリロードすることもできます(プロパティの割り当てごとにそれぞれをポップします)が、ローカルの使用がはるかに優れたスタックを悪用します。

また、単一のローカルを使用することもできます。これは、それぞれが単純に書き込まれ、配列インデックスによってリストに格納されるため、ローカルが再び必要になることはありません。これは、C#チームが検討するものかもしれません-Eric Lippertがこのスレッドでつまずいた場合、これについていくつか考えているかもしれません。

さて、この調査はまた、あなたの非常に大規模な方法のためのローカルのこの使用を回避する潜在的なルートを私たちに与えます:イテレータを使用してください:

public Foo[] GetInts()
{
    return GetIntsHelper().ToArray();
}

private IEnumerable<Foo> GetIntsHelper()
{
    yield return new Foo() { Bar = 1 };
    yield return new Foo() { Bar = 2 };
    yield return new Foo() { Bar = 3 };
    yield return new Foo() { Bar = 4 };
    yield return new Foo() { Bar = 5 };
}

今のところ、ILはGetInts()単に.maxstack 8頭にあり、地元の人はいません。イテレータ関数GetIntsHelper()を見ると、次のようになっています。

.maxstack 2
.locals init (
    [0] class SomeExample/'<GetIntsHelper>d__5'
)

だから今、私たちはそれらのメソッドでそれらすべてのローカルを使用するのをやめました...

しかし..。

コンパイラによって自動的に生成されたクラスSomeExample/'<GetIntsHelper>d__5'を見てください-そして、ローカルがまだそこにあることがわかります-それらはちょうどそのクラスのフィールドにプロモートされました:

.field public class SomeExample/Foo '<>g__initLocal0'
.field public class SomeExample/Foo '<>g__initLocal1'
.field public class SomeExample/Foo '<>g__initLocal2'
.field public class SomeExample/Foo '<>g__initLocal3'
.field public class SomeExample/Foo '<>g__initLocal4'

したがって、問題は、シナリオに適用した場合、そのオブジェクトの作成もスタックを爆破するのでしょうか?おそらくそうではありません。メモリ内では、大規模な配列を初期化しようとするようなものである必要があります。100万要素の配列は非常に受け入れられます(実際には十分なメモリがあると想定しています)。

つまり、各要素のメソッドを使用するように移行するだけで、コードを非常に簡単に修正できる可能性があります。IEnumerableyield

ただし、ベストプラクティスでは、これを静的に定義する必要がある場合は、データをディスク上の埋め込みリソースまたはファイルに追加し(XMLおよびLinq to XMLが適切な選択である可能性があります)、オンデマンドでそこからロードすることを検討してください。

さらに良い-データベースに貼り付けてください:)

于 2012-05-24T11:52:23.507 に答える
0

コントローラのDataHelper.Sellersにどのようにアクセスしますか?このコントローラにGEtまたはPOSTを使用していますか?このような大量のデータには、POSTを使用する必要があります。

また、IISスタックサイズを確認する必要があります:http://blogs.msdn.com/b/tom/archive/2008/03/31/stack-sizes-in-iis-affects-asp-net.aspx

ASP.NETのアプリケーションプールで32ビットアプリケーションを有効にしてみてください。

于 2012-05-24T11:41:50.073 に答える