2011-02-02 5 views
29

私は次のコードを実行したとき、私はStackOverflowExceptionを得る:StackOverflowExceptionを引き起こしている可能性が何.NETでStackOverflowExceptionの原因を追跡するにはどうすればよいですか?

private void MyButton_Click(object sender, EventArgs e) { 
    MyButton_Click_Aux(); 
} 

private static volatile int reportCount; 

private static void MyButton_Click_Aux() { 
    try { /*remove because stack overflows without*/ } 
    finally { 
    var myLogData = new ArrayList(); 
    myLogData.Add(reportCount); 
    myLogData.Add("method MyButtonClickAux"); 
    Log(myLogData); 
    } 
} 

private static void Log(object logData) { 
    // my log code is not matter 
} 

を?

+34

"それゆえに.netのバグでなければなりません"おそらくそうではありません。 – jason

+8

「MyButton_Click_Aux」のコードを投稿してください –

+7

MyButton_Click_Auxにコードを含めてください。 –

答えて

44

私はそれはそれは(まだ)が発生し、なぜ私は知らない

を起こってからそれを停止する方法を知っています。そして、あなたは実際に.Net BCLか、おそらくはJITのいずれかでバグを発見したようです。

私はちょうどMyButton_Click_Auxメソッドのすべての行をコメントアウトし、次にそれらを1つずつ戻し始めました。

static intからvolatileを削除すると、StackOverflowExceptionが返されなくなります。今

明らかにメモリバリアとは何かが問題の原因となっている理由は...研究する - おそらく何らかの形で自分自身を呼び出すためのMyButton_Click_Aux方法を強制的に...

UPDATE

わかりましたので、他の人NET 3.5は問題ではないことが判明しています。

私もこれらのコメントは、それに関連して.Nt 4を使用しています:私が言ったように

は、揮発性のオフを取り、それが動作します。あなたは背中の揮発を入れて、削除する場合

同様に、試しては最終的に、それはまた、作品/:

private static void MyButton_Click_Aux() 
{ 
    //try { /*remove because stack overflows without*/ } 
    //finally 
    //{ 
    var myLogData = new ArrayList(); 
    myLogData.Add(reportCount); 
    //myLogData.Add("method MyButtonClickAux"); 
    //Log(myLogData); 
    //} 
} 

試してみるが、最終的には/とき初期化されていないreportCountとは何かだった場合、私も疑問に思いましたしかし、それをゼロに初期化すれば、違いはありません。私は今、ILで探しています

- それは参加するためにいくつかのASMのチャップスを持つ人を必要とするかもしれませんが...私が言うように、これは本当にの分析を必要とする予定です

最終更新実際に何が起こっているのかを理解するためのJIT出力と、アセンブラを分析するのが楽しいと感じている間に、おそらくMicrosoftの誰かの仕事だと感じています。それは非常に狭い状況のようです。

私はリリースビルドに移行して、分析のためにすべてのILノイズ(nopsなど)を取り除きました。

しかし、これは診断に複雑な影響を与えました。私はそれを持っていると思ったが、しなかった - しかし、今私はそれが何であるか知っている。揮発性としてint型で

private static void MyButton_Click_Aux() 
{ 
    try { } 
    finally 
    { 
    var myLogData = new ArrayList(); 
    Console.WriteLine(reportCount); 
    //myLogData.Add("method MyButtonClickAux"); 
    //Log(myLogData); 
    } 
} 

私はこのコードを試してみました。それは間違いなく実行されます。ここではILだ:

.maxstack 1 
L_0000: leave.s L_0015 
L_0002: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() 
L_0007: pop 
L_0008: volatile. 
L_000a: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Form1::reportCount 
L_000f: call void [mscorlib]System.Console::WriteLine(int32) 
L_0014: endfinally 
L_0015: ret 
.try L_0000 to L_0002 finally handler L_0002 to L_0015 

その後、我々は再びエラーを取得するために必要な最小限のコードを見て:

private static void MyButton_Click_Aux() 
{ 
    try { } 
    finally 
    { 
    var myLogData = new ArrayList(); 
    myLogData.Add(reportCount); 
    } 
} 

そして、それはILます:

.maxstack 2 
.locals init (
    [0] class [mscorlib]System.Collections.ArrayList myLogData) 
L_0000: leave.s L_001c 
L_0002: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() 
L_0007: stloc.0 
L_0008: ldloc.0 
L_0009: volatile. 
L_000b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Form1::reportCount 
L_0010: box int32 
L_0015: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) 
L_001a: pop 
L_001b: endfinally 
L_001c: ret 
.try L_0000 to L_0002 finally handler L_0002 to L_001c 

違いは?まあ、揮発性のintのボクシングとバーチャルコール - 私が発見した2つです。だから私のセットアップこの2つのクラス:

public class DoesNothingBase 
{ 
    public void NonVirtualFooBox(object arg) { } 
    public void NonVirtualFooNonBox(int arg) { } 

    public virtual void FooBox(object arg) { } 
    public virtual void FooNonBox(int arg) { } 
} 

public class DoesNothing : DoesNothingBase 
{ 
    public override void FooBox(object arg) { } 
    public override void FooNonBox(int arg) { } 
} 

そして問題のある方法のこれらの4つのバージョンのそれぞれを試してみました:作品

try { } 
finally 
{ 
    var doesNothing = new DoesNothing(); 
    doesNothing.FooNonBox(reportCount); 
} 

try { } 
finally 
{ 
    var doesNothing = new DoesNothing(); 
    doesNothing.NonVirtualFooNonBox(reportCount); 
} 

これも機能します。

try { } 
finally 
{ 
    var doesNothing = new DoesNothing(); 
    doesNothing.FooBox(reportCount); 
} 

おっと - StackOverflowException

そして:再び

try { } 
finally 
{ 
    var doesNothing = new DoesNothing(); 
    doesNothing.NonVirtualFooBox(reportCount); 
} 

おっと! StackOverflowException

私はこれをさらに進めることができました - しかし、問題は明らかにtry/catchのfinallyブロックの内側にある揮発性intのボクシングによって引き起こされたと思います。問題ない。私はcatch節を追加し(そしてそこにコードを入れた)、問題はなかった。

それは私が推測する他の値の種類のボクシングにも適用できます。

したがって、.Net 4.0では、デバッグとリリースビルドの両方で要約すると、finallyブロック内の揮発性intのボクシングは、JITがスタックを満たす最後のコードを生成するように見えます。スタックトレースが単に「外部コード」を表示しているという事実も、この命題をサポートしています。

try/finallyによって生成されるコードのレイアウトやサイズによっては、必ずしも再現できない可能性もあります。これは、間違った場所に誤って生成された誤ったjmpなどと明らかに関係しており、最終的にスタックに1つ以上のプッシュコマンドを繰り返します。それが実際にボックス操作によって引き起こされているという考えは、率直に、魅力的です!

決勝最終更新

あなたは@Hasty Gが見つかっMS接続のバグ(さらに下に答える)を見れば - あなたがそこに見ること揮発性ブールのあるが、同様の方法でバグマニフェスト、 キャッチステートメント。

また、MSはこれをreproで入手した後に修正プログラムをキューイングしましたが、まだ7か月後に修正プログラムはありません。私はMS Connectのサポートとして以前に記録に残っているので、私はもう言わないだろう - 私は必要とは思わない!

決勝決勝決勝更新(23/02/2011)

それは固定されている - が、まだリリースされていません。 MS ConnectバグのMSチームからのお見積もり:

はい、修正されました。私たちは、修正プログラムをどのように配布するのが最適かを把握しています。これは4.5で修正されていますが、4.5リリース前にコード生成バグのバッチを修正したいと思っています。

+0

@Andrasゾルタン私は揮発性のキーワードを考えていませんでした。以前は多くのスレッドで使用されていましたが、もはや目的がありませんでしたので、私は非常に奇妙な問題を取り除くことができます –

+0

@PRASHANT P - 私はまた、ブロックの周りからtry \ finallyを削除し、揮発性を残しました - そしてちょっと前のステート - もうStackOverflow ... –

+0

ええ、私は試しを削除/最終的にそれを取り除いて気づいた。私はあなたがそこに最初にそこにいた理由があると思ったとき、私はそれを示唆しませんでした。 – dotalchemy

12

あなたのコードにはバグがあります。恐らく、MyButton_Click_Aux()は、何らかの方法を再入力させるでしょう。しかし、あなたはあなたの質問からそのコードを不可解に省略したので、誰もそれにコメントすることはできません。

+0

@Jonathan Wood私は再帰を知っています。私は多くのCコーディング経験があります。これはフォームでしかないと思います。 –

+0

@PRASHANT:いいえ、あなたは確立していませんそれ。私はあなたの質問のコードを更新してうれしいですが、表示されていない 'try'ブロックには何が入っていますか?コメントから、コードの欠落がオーバーフローの原因であることを理解していますか?とにかく、Visual Studioには素敵なデバッガがあります。コードをステップ実行します。再入力がないかどうかを確認し、そうでない場合は、エラーの発生場所を正確に確認することができます。 –

+0

@Johnathan Wood私はこのコードをtryでやったことはありません。私は単純に1つのボタンでフォームを作成し、スタックトレースは.net privatesしか持っていません。 –

0

ログはログに戻されますか?これはまた、SOを引き起こす。

+0

@マークアベニウスこれは明らかです。私は編集してログからすべてのコードを削除していますが、それでも電話がありません –

+0

@Prashant:それにもかかわらず、SOが発生する可能性がある場所だから、「ログ」が何をしているのか分からないので、私はそれを言いました。ちょうど助けようとしています... –

+1

@マークアベニウス私は申し訳ありません私は多くの反応で多くの皮肉を受けています私は侮辱を意図しません –

0

例外が発生した場合、コールスタックパネルに記録された内容を確認してください。コールスタック自体は、多くを伝えることができます。

さらに、SOS.dllとWinDbgを使用した低レベルのデバッグでも、多くのことを知ることができます。

関連する問題