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


115

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

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

関連するかどうかはわかりませんが、追加情報:

  • メインスレッドで例外がスローされない

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


3
@RichardOD、それはバグだったので、必ずバグを修正します。ただし、問題は別の方法で表示される可能性があり、私はそれを処理したい
Toto

7
同意しますが、スタックオーバーフローは、キャッチすべきはないのでキャッチできない深刻なエラーです。代わりに、壊れたコードを修正してください。
Ian Kemp、

11
@RichardOD:たとえば、再帰下降パーサーを設計して、ホストマシンが実際に必要とするものを超えて深さに人工的な制限を課したくない場合は、どうすればよいですか?もし私がドラチャーを持っているなら、明示的にキャッチできるStackCritical例外があり、それでもスタックスペースが少し残っている間に発生します。実際にスローされるまで無効になり、安全な量のスタックスペースが残るまでキャッチされません。
スーパーキャット

2
この質問は便利です-スタックオーバーフロー例外が発生した場合、ユニットテストに失敗します-しかし、NUnitは、他の例外の場合のように失敗するのではなく、テストを「無視された」カテゴリに移動します-キャッチする必要がありますそして、やるAssert.Fail代わりに。真剣に-これについてどうやって行くのですか?
BrainSlugs83 2014

回答:


109

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

  1. CLRは、ホストされた環境で実行されています*ホストでは、StackOverflow例外の処理を明確に許可しています
  2. stackoverflow例外は、実際のスタックオーバーフロー状況が原因ではなく、ユーザーコードによってスローされます(参照

*「私のコードはCLRをホストし、CLRのオプションを構成します」のような「ホストされた環境」ではなく、「私のコードは共有ホスティングで実行されます」


27
関連するシナリオで捕捉できない場合、なぜStackoverflowExceptionオブジェクトが存在するのですか?
マヌ

9
@Manuには少なくともいくつかの理由があります。1)1.1で捕まえることができるので、目的があったということです。2)それはまだそれがまだ有効例外タイプですので、あなたがCLRをホストしている場合はキャッチすることができます
JaredPar

3
キャッチできない場合...何が起こったかを説明するWindowsイベントに、デフォルトで完全なスタックトレースが含まれていないのはなぜですか?

10
StackOverflowExceptionsをホスト環境で処理できるようにするにはどうすればよいですか?私が求めている理由は、ホストされた環境を実行しているためであり、アプリプール全体が破壊されるという正確な問題が発生しています。私はむしろそれをスレッドに中止させたいと思います。そこでスレッドは先頭に巻き戻され、エラーをログに記録して、すべてのアプリプールのスレッドを強制終了せずに続行できます。
Brain2000 2014年

Starting with 2.0 ...、私は好奇心が強いのですが、彼らがSOをキャッチするのを妨げているものは何1.1ですか?
M.kazem Akhgary 2017年

47

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

より大きなスタックを自分に与えることができます:-

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

チーズをとってください。;)


47
Cheese特定からはほど遠いです。私は行くだろうthrow new CheeseException("Gouda");
C.Evenhuis

13
ゴーダはそれがRollingCheeseException(「ダブルグロスター」)である必要があり、例外チーズであることは疑いがありますなくなりC.Evenhuis @本当に見cheese-rolling.co.ukを

3
それは2をたまたまそれをどこに引くことなく、あなたが頻繁に知らないので笑、1)固定は、スタックサイズを大きくすることで無用である)ことはできません無限再帰ANDM 3)右の場所にスタックをチェックする最初のようなものです
フィーロ

2
しかし、私は乳糖不耐症です
redoc

39

StackOverflowExceptionのMSDNページから:

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

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


23

すでにいくつかのユーザーが言っているように、例外をキャッチすることはできません。ただし、それが発生している場所を見つけるのに苦労している場合は、ビジュアルスタジオがスローされたときに中断するように構成することをお勧めします。

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

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


1
デバッグ-VS 2015の例外はどこにありますか?
FrenkyB 2017

1
デバッグ-Windows-例外設定
Simon

15

上記のように、プロセス状態が破損しているためにシステムによって発生した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以降、このようなサイレントエラーの累積的な影響には、パフォーマンスの低下、データの破損、ロックアップなどが含まれており、デバッグが困難だったため、子スレッドの未処理の例外に対するこのバックストップが削除されました。ランタイムが終了しない場合のリストを含む詳細については、マネージスレッドの例外を参照してください。


6

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

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


CLR 2.0以降、a StackOverflowExceptionはデフォルトでプロセスを終了します。
ブライアンラスムッセン2015年

いいえ。OOMをキャッチできますが、場合によってはそれを行うのが理にかなっています。スレッドが消えてどういう意味かわかりません。スレッドに未処理の例外がある場合、CLRはプロセスを終了します。スレッドがそのメソッドを完了すると、クリーンアップされます。
ブライアンラスムッセン2015年

5

できません。CLRはあなたをさせません。スタックオーバーフローは致命的なエラーであり、回復できません。


では、キャッチ可能ではなくユニットテストランナーがクラッシュした場合、この例外でユニットテストをどのように失敗させるのでしょうか。
BrainSlugs83 2014

1
@ BrainSlugs83。それはばかげた考えだからです。とにかくStackOverflowExceptionでコードが失敗するかどうかをテストするのはなぜですか?より深いスタックを処理できるようにCLRが変更されるとどうなりますか?すでに深くネストされたスタックがある場所でユニットテスト済みの関数を呼び出すとどうなりますか?テストできないもののようです。手動でスローしようとしている場合は、タスクに適した例外を選択してください。
Matthew Scharley、2014

5

ほとんどの投稿で説明できないので、別の領域を追加しましょう。

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


3

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

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


2
「catch」ステートメントは実際には問題になりません。catchステートメントが実行できるまでに、システムが2つのスタックスペースを使おうとした場合の影響をロールバックしてしまうからです。スタックオーバーフローの例外をキャッチすることが危険である必要がある理由はありません。それらをキャッチできない理由は、それらを安全にキャッチできるようにするには、オーバーフローしなくても、スタックを使用するすべてのコードにオーバーヘッドを追加する必要があるためです。
スーパーキャット2013

4
ある時点で、ステートメントはよく考えられていません。Stackoverflowをキャッチできない場合、本番環境でどこで発生したかわからない可能性があります。
Offler
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.