web-dev-qa-db-ja.com

C#はスタックオーバーフロー例外をキャッチします

スタックオーバーフロー例外をスローするメソッドを再帰的に呼び出しました。最初の呼び出しはtry catchブロックに囲まれていますが、例外はキャッチされません。

スタックオーバーフロー例外は特別な方法で動作しますか?例外を適切にキャッチ/処理できますか?

注意:関連する場合:

  • 例外はメインスレッドでスローされません

  • コードが例外をスローしているオブジェクトは、Assembly.LoadFrom(...)。CreateInstance(...)によって手動でロードされます

106
Toto

2.0以降、StackOverflow例外は次の状況でのみキャッチできます。

  1. CLRはホスト環境で実行されています* ホストがStackOverflow例外の処理を特に許可している場合
  2. Stackoverflow例外は、実際のスタックオーバーフローの状況ではなく、ユーザーコードによってスローされます( Reference

*「私のコードはCLRをホストし、CLRのオプションを構成します」ではなく、「ホストされた環境」。

101
JaredPar

正しい方法はオーバーフローを修正することですが、...

あなたは自分にもっと大きなスタックを与えることができます:-

using System.Threading;
Thread T = new Thread(threadDelegate, stackSizeInBytes);
T.Start();

System.Diagnostics.StackTrace FrameCountプロパティを使用して、使用したフレームをカウントし、フレームの制限に達したときに独自の例外をスローできます。

または、残りのスタックのサイズを計算し、しきい値を下回ったときに独自の例外をスローできます。

class Program
{
    static int n;
    static int topOfStack;
    const int stackSize = 1000000; // Default?

    // The func is 76 bytes, but we need space to unwind the exception.
    const int spaceRequired = 18*1024; 

    unsafe static void Main(string[] args)
    {
        int var;
        topOfStack = (int)&var;

        n=0;
        recurse();
    }

    unsafe static void recurse()
    {
        int remaining;
        remaining = stackSize - (topOfStack - (int)&remaining);
        if (remaining < spaceRequired)
            throw new Exception("Cheese");
        n++;
        recurse();
    }
}

チーズをキャッチしてください。 ;)

44
user159335

StackOverflowException sのMSDNページから:

.NET Frameworkの以前のバージョンでは、アプリケーションでStackOverflowExceptionオブジェクトをキャッチできました(たとえば、無制限の再帰から回復するため)。ただし、スタックオーバーフローの例外を確実にキャッチしてプログラムの実行を継続するには、かなりの追加コードが必要になるため、この方法は現在推奨されていません。

.NET Frameworkバージョン2.0以降、StackOverflowExceptionオブジェクトはtry-catchブロックでキャッチできず、対応するプロセスはデフォルトで終了します。したがって、スタックオーバーフローを検出して防止するためのコードを記述することをお勧めします。たとえば、アプリケーションが再帰に依存している場合、カウンターまたは状態条件を使用して再帰ループを終了します。共通言語ランタイム(CLR)をホストするアプリケーションは、CLRがスタックオーバーフロー例外が発生したアプリケーションドメインをアンロードし、対応するプロセスを続行するように指定できることに注意してください。詳細については、「ICLRPolicyManagerインターフェイス」および「共通言語ランタイムのホスト」を参照してください。

37

すでにいくつかのユーザーが言っているように、例外をキャッチすることはできません。ただし、それがどこで発生しているかを見つけるのに苦労している場合は、Visual Studioがスローされたときに壊れるように構成することができます。

そのためには、「デバッグ」メニューから例外設定を開く必要があります。 Visual Studioの古いバージョンでは、これは「デバッグ」-「例外」にあります。新しいバージョンでは、「デバッグ」-「Windows」-「例外設定」にあります。

設定を開いたら、「共通言語ランタイムの例外」を展開し、「システム」を展開して、下にスクロールして「System.StackOverflowException」を確認します。次に、呼び出しスタックを見て、呼び出しの繰り返しパターンを探すことができます。これにより、スタックオーバーフローの原因となっているコードを修正するための参照先がわかります。

21
Simon

上記で何度か述べたように、破損したプロセス状態が原因でシステムによって発生したStackOverflowExceptionをキャッチすることはできません。ただし、例外をイベントとして通知する方法があります。

http://msdn.Microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

.NET Frameworkバージョン4以降、このイベントは、イベントハンドラーがセキュリティクリティカルで、HandleProcessCorruptedStateExceptionsAttribute属性を持たない限り、スタックオーバーフローやアクセス違反など、プロセスの状態を破損する例外に対して発生しません。

それでも、イベント関数を終了した後、アプリケーションは終了します(非常に汚い回避策は、このイベント内でアプリを再起動することでしたが、そうしないでください)。ただし、ロギングには十分です!

.NET Frameworkバージョン1.0および1.1では、メインアプリケーションスレッド以外のスレッドで発生する未処理の例外はランタイムによってキャッチされるため、アプリケーションは終了しません。したがって、アプリケーションを終了せずにUnhandledExceptionイベントが発生する可能性があります。 .NET Frameworkバージョン2.0から、子スレッドの未処理の例外に対するこのバックストップは削除されました。このようなサイレント障害の累積的な影響には、パフォーマンスの低下、データの破損、ロックアップが含まれていたためです。ランタイムが終了しないケースのリストなど、詳細については、「マネージスレッドの例外」を参照してください。

15
FooBarTheLittle

はい、CLR 2.0スタックオーバーフローからの回復不能な状況と見なされます。したがって、ランタイムはまだプロセスをシャットダウンします。

詳細については、ドキュメントを参照してください http://msdn.Microsoft.com/en-us/library/system.stackoverflowexception.aspx

6
Brian Rasmussen

できません。 CLRはあなたを許可しません。スタックオーバーフローは致命的なエラーであり、回復することはできません。

5

ほとんどの投稿で説明されているように、別の領域を追加できます。

多くのWebサイトでは、これを回避する方法は別のAppDomainを使用しているため、これが発生するとドメインがアンロードされると言う人がいます。 CLRの既定の動作ではKillProcessイベントが発生し、既定のAppDomainがダウンするため、これは絶対に間違っています(CLRをホストしていない限り)。

5
No hay Problema

それは不可能であり、それには十分な理由があります(1つは、catch(Exception){}について考えてみてください)。

スタックオーバーフロー後も実行を継続する場合は、別のAppDomainで危険なコードを実行します。元のドメインに影響を与えることなく、オーバーフロー時に現在のAppDomainを終了するようにCLRポリシーを設定できます。

3
ima