C #에서 스택 오버플로 예외 포착


115

스택 오버플로 예외를 발생시키는 메서드에 대한 재귀 호출이 있습니다. 첫 번째 호출은 try catch 블록으로 둘러싸여 있지만 예외는 포착되지 않습니다.

스택 오버플로 예외가 특별한 방식으로 작동합니까? 예외를 제대로 포착 / 처리 할 수 ​​있습니까?

관련성이 있는지 확실하지 않지만 추가 정보 :

  • 주 스레드에서 예외가 발생하지 않습니다.

  • 코드에서 예외가 발생하는 개체는 Assembly.LoadFrom (...). CreateInstance (...)에 의해 수동으로로드됩니다.


3
@RichardOD, 버그이기 때문에 버그를 수정했습니다. 그러나 문제는 다른 방식으로 나타날 수 있으며 처리하고 싶습니다
Toto

7
합의, 스택 오버플로가 있기 때문에 잡힐 수없는 심각한 오류가 잡힐. 대신 손상된 코드를 수정하십시오.
Ian Kemp

11
@RichardOD : 예를 들어 재귀 하강 파서를 설계하고 호스트 머신에서 실제로 요구하는 것 이상의 깊이에 인위적인 제한을 부과하지 않으려면 어떻게해야합니까? 내 druthers가 있다면 명시 적으로 잡을 수있는 StackCritical 예외가있을 것입니다.이 예외는 여전히 약간의 스택 공간이 남아있는 동안 실행될 것입니다. 실제로 던져 질 때까지 자동으로 비활성화되고 안전한 양의 스택 공간이 남을 때까지 잡힐 수 없습니다.
supercat

2
이 질문은 유용합니다. 스택 오버플로 예외가 발생하면 단위 테스트에 실패하고 싶습니다. 그러나 NUnit은 다른 예외와 마찬가지로 실패하는 대신 테스트를 "무시 됨"범주로 이동합니다. Assert.Fail대신 수행하십시오 . 진지하게-우리는 어떻게해야합니까?
BrainSlugs83

답변:


109

2.0부터 StackOverflow 예외는 다음 상황에서만 포착 될 수 있습니다.

  1. CLR은 호스팅 된 환경에서 실행되는 * 호스트가 특별히 처리 할 수 StackOverflow의 예외를 허용 곳
  2. stackoverflow 예외는 실제 스택 오버플로 상황이 아닌 사용자 코드에 의해 발생합니다 ( 참조 ).

* "내 코드가 CLR을 호스팅하고 CLR의 옵션을 구성합니다"와 같은 "호스팅 된 환경"이 아니라 "내 코드가 공유 호스팅에서 실행 됨"이 아닙니다.


27
관련 시나리오에서 포착 할 수없는 경우 StackoverflowException 객체가 존재하는 이유는 무엇입니까?
Manu

9
적어도 몇 가지 이유로 @Manu. 1) 1.1에서 잡힐 수 있고 따라서 목적이 있다는 것입니다. 2) CLR을 호스팅하는 경우 여전히 잡힐 수 있으므로 여전히 유효한 예외 유형입니다
JaredPar

3
잡을 수 없다면 ... 무슨 일이 일어 났는지 설명하는 Windows 이벤트에 기본적으로 전체 스택 추적이 포함되지 않는 이유는 무엇입니까?

10
호스팅 된 환경에서 StackOverflowExceptions를 처리하도록 허용하는 방법은 무엇입니까? 내가 묻는 이유는 호스팅 된 환경을 실행하고 있고 전체 앱 풀을 파괴하는 정확한 문제가 있기 때문입니다. 오히려 스레드를 중단하고 맨 위로 다시 풀릴 수 있으며 그런 다음 오류를 기록하고 모든 apppool의 스레드를 죽이지 않고 계속할 수 있습니다.
Brain2000 2014 년

Starting with 2.0 ..., 나는 호기심입니다. 무엇이 그들이 그렇게 잡는 것을 방해하고 어떻게 가능했는지 1.1(당신의 의견에서 언급했습니다)?
M.kazem Akhgary

47

올바른 방법은 오버플로를 수정하는 것이지만 ....

자신에게 더 큰 스택을 줄 수 있습니다.

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

System.Diagnostics.StackTrace FrameCount 속성을 사용하여 사용한 프레임 수를 계산하고 프레임 제한에 도달하면 고유 한 예외를 throw 할 수 있습니다.

또는 남은 스택의 크기를 계산하고 임계 값 아래로 떨어질 때 자체 예외를 throw 할 수 있습니다.

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구체적이지 않습니다. I 'd go forthrow new CheeseException("Gouda");
C.Evenhuis

13
@ C.Evenhuis Gouda가 예외적 인 치즈라는 것은 의심의 여지가 없지만 RollingCheeseException ( "Double Gloucester")이어야합니다. 실제로 cheese-rolling.co.uk

3
ㅋㅋ, 1) 잡히지 않으면 어디서 일어나는지 잘 모르기 때문에 수정이 불가능하다. 2) 끝없는 재귀로 Stacksize를 늘리는 것은 쓸모없고 m 3) 올바른 위치에서 Stack을 확인하는 것은 처음과 같다
Firo

2
하지만 나는 유당 불내성입니다
redoc

39

StackOverflowException 의 MSDN 페이지에서 :

.NET Framework의 이전 버전에서는 애플리케이션이 StackOverflowException 개체를 포착 할 수있었습니다 (예 : 제한되지 않은 재귀에서 복구하기 위해). 그러나 스택 오버플로 예외를 안정적으로 포착하고 프로그램 실행을 계속하려면 상당한 추가 코드가 필요하기 때문에 현재이 방법은 권장되지 않습니다.

.NET Framework 버전 2.0부터 StackOverflowException 개체는 try-catch 블록에서 포착 할 수 없으며 해당 프로세스는 기본적으로 종료됩니다. 따라서 사용자는 스택 오버플로를 감지하고 방지하기 위해 코드를 작성하는 것이 좋습니다. 예를 들어 애플리케이션이 재귀에 의존하는 경우 카운터 또는 상태 조건을 사용하여 재귀 루프를 종료합니다. CLR (공용 언어 런타임)을 호스팅하는 응용 프로그램은 스택 오버플로 예외가 발생하는 응용 프로그램 도메인을 CLR이 언로드하고 해당 프로세스를 계속하도록 지정할 수 있습니다. 자세한 내용은 ICLRPolicyManager 인터페이스 및 공용 언어 런타임 호스팅을 참조하십시오.


23

여러 사용자가 이미 말했듯이 예외를 포착 할 수 없습니다. 그러나 어디서 발생하는지 파악하는 데 어려움을 겪고 있다면 Visual Studio가 던져 질 때 중단되도록 구성 할 수 있습니다.

이를 위해서는 '디버그'메뉴에서 예외 설정을 열어야합니다. 이전 버전의 Visual Studio에서는 '디버그'- '예외'에 있습니다. 최신 버전에서는 '디버그'- 'Windows'- '예외 설정'에 있습니다.

설정이 열리면 '공용 언어 런타임 예외'를 확장하고 '시스템'을 확장 한 다음 아래로 스크롤하여 'System.StackOverflowException'을 확인합니다. 그런 다음 호출 스택을보고 반복되는 호출 패턴을 찾을 수 있습니다. 그러면 스택 오버플로를 유발하는 코드를 수정할 위치를 알 수 있습니다.


1
디버그-VS 2015의 예외는 어디에 있습니까?
FrenkyB

1
디버그 - 윈도우 - 예외 설정
사이먼

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는 기본적으로 프로세스를 종료합니다.
Brian Rasmussen

아니요. OOM을 잡을 수 있으며 경우에 따라 그렇게하는 것이 합리적 일 수 있습니다. 실이 사라지는 것이 무슨 뜻인지 모르겠습니다. 스레드에 처리되지 않은 예외가있는 경우 CLR은 프로세스를 종료합니다. 스레드가 메서드를 완료하면 정리됩니다.
Brian Rasmussen

5

당신은 할 수 없습니다. CLR은 당신을 허용하지 않습니다. 스택 오버플로는 치명적인 오류이며 복구 할 수 없습니다.


그렇다면 catchable 대신 단위 테스트 실행기를 대신 충돌시키는 경우이 예외에 대해 단위 테스트가 실패하게 만드는 방법은 무엇입니까?
BrainSlugs83 2014 년

1
@ BrainSlugs83. 어리석은 생각이기 때문입니다. 어쨌든 StackOverflowException으로 코드가 실패하면 왜 테스트하고 있습니까? 더 깊은 스택을 처리 할 수 ​​있도록 CLR이 변경되면 어떻게됩니까? 이미 깊이 중첩 된 스택이있는 곳에서 단위 테스트 함수를 호출하면 어떻게됩니까? 테스트 할 수없는 것 같습니다. 수동으로 던지려는 경우 작업에 대해 더 나은 예외를 선택하십시오.
매튜 Scharley

5

대부분의 게시물에서 설명 할 수 없으므로 다른 영역을 추가하겠습니다.

많은 웹 사이트에서이를 방지하는 방법은 다른 AppDomain을 사용하는 것이므로이 경우 도메인이 언로드됩니다. CLR의 기본 동작으로 인해 KillProcess 이벤트가 발생하여 기본 AppDomain이 중단되므로 CLR을 호스팅하지 않는 한 완전히 잘못된 것입니다.


3

불가능하고 그럴만한 이유가 있습니다 (하나는 모든 catch (Exception) {}에 대해 생각해보십시오).

스택 오버플로 후에도 실행을 계속하려면 다른 AppDomain에서 위험한 코드를 실행하십시오. 원래 도메인에 영향을주지 않고 오버플로시 현재 AppDomain을 종료하도록 CLR 정책을 설정할 수 있습니다.


2
"catch"문은 실제로 문제가되지 않을 것입니다. catch 문이 실행될 때 쯤이면 시스템이 두 개의 스택 공간을 사용하려고 시도한 결과를 롤백했기 때문입니다. 스택 오버플로 예외를 포착하는 것이 위험해야 할 이유가 없습니다. 이러한 예외가 잡힐 수없는 이유는 그들이 안전하게 잡히려면 오버플로되지 않더라도 스택을 사용하는 모든 코드에 약간의 추가 오버 헤드를 추가 해야하기 때문입니다.
supercat 2013-12-05

4
어떤 시점에서 그 진술은 잘 생각되지 않습니다. Stackoverflow를 잡을 수 없다면 프로덕션 환경에서 어떤 일이 발생했는지 모를 것입니다.
Offler
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.