StackOverflowException в .NET

После нескольких StackOverflowExceptions в .NET я заметил, что они полностью обходят необработанные обработчики исключений, которые предлагает .NET (Application.ThreadException / AppDomain.UnhandledException). Это очень беспокоит, поскольку у нас есть критический код очистки в этих обработчиках исключений.

Есть ли способ преодолеть это?


person adams    schedule 20.09.2008    source источник


Ответы (4)


Не совсем; переполнение стека или исключение нехватки памяти в самой среде CLR означает, что что-то пошло не так (я обычно получаю это, когда был тупицей и создал рекурсивное свойство).

Когда возникает это состояние, среда CLR не может выделить новые вызовы функций или память, чтобы позволить ей вызывать обработчики исключений; это сценарий «мы должны остановить сейчас».

Однако если вы сами вызовете исключение, будут вызваны ваши обработчики исключений.

person blowdart    schedule 20.09.2008
comment
Я нахожу это несколько удивительным, ведь clr - это код, принадлежащий Microsoft, они могли бы сэкономить несколько кадров стека, чтобы разрешить обработку исключений. - person adams; 20.09.2008
comment
Как он узнает, сколько нужно зарезервировать? В ситуации переполнения стека сам стек вырос до такого размера, что с ним невозможно справиться. Зарезервировав некоторое пространство, вам нужно будет зарезервировать максимальный размер стека, который, в свою очередь, составит половину стека, доступного для программного обеспечения. - person blowdart; 20.09.2008
comment
Хорошо, я понял эту идею ... Типа ... В однопоточной среде. Но ведь мы могли бы создать более одного потока, не так ли? И у каждого потока есть отдельный стек, не так ли? Тогда зачем завершать весь процесс, если только один поток исчерпал стек? - person Fyodor Soikin; 29.08.2010
comment
Обычно я получаю это, когда был тупицей и создал рекурсивное свойство - спасибо за это. Это буквально указывало мне на то, в чем проблема. Имея набор вычисляемых свойств, одни полагаются на другие ... одна маленькая ошибка и бум - исключение OOM. - person Tommy; 15.11.2016

Есть три вида так называемых «асинхронных исключений». Это ThreadAbortException, OutOfMemoryException и упомянутое StackOverflowException. Эти исключения могут возникать при любой инструкции в вашем коде.

И есть способ их преодолеть:

Самый простой - ThreadAbortException. Когда текущий код выполняется в finally-блоке. ThreadAbortExceptions как бы «перемещены» в конец блока finally. Таким образом, все в блоке finally не может быть прервано исключением ThreadAbortException.

Чтобы избежать OutOfMemoryException, у вас есть только одна возможность: ничего не размещать в куче. Это означает, что вам не разрешено создавать какие-либо новые ссылочные типы.

Чтобы преодолеть StackOverflowException, вам понадобится помощь от Framework. Эта помощь проявляется в областях ограниченного выполнения. Требуемый стек выделяется до фактического выполнения кода и дополнительно гарантирует, что код уже JIT-скомпилирован и поэтому доступен для выполнения.

Есть три формы для выполнения кода в ограниченных областях выполнения (скопированы из Блог команды BCL):

  • ExecuteCodeWithGuaranteedCleanup, безопасная для переполнения стека форма try / finally.
  • Блоку try / finally непосредственно предшествует вызов RuntimeHelpers.PrepareConstrainedRegions. Блок try не ограничен, но есть все блоки catch, finally и fault для этой попытки.
  • As a critical finalizer - any subclass of CriticalFinalizerObject has a finalizer that is eagerly prepared before an instance of the object is allocated.
    • A special case is SafeHandle's ReleaseHandle method, a virtual method that is eagerly prepared before the subclass is allocated, and called from SafeHandle's critical finalizer.

Вы можете найти больше в этих сообщениях блога:

Ограниченные области выполнения и другие ошибки [Брайан Грюнкемейер] на блог команды BCL.

Журнал Джо Даффи о сбоях атомарности и асинхронных исключений, где он дает очень хороший обзор асинхронных исключений и надежности в .NET Framework.

person Thomas Danecker    schedule 20.09.2008
comment
Обратите внимание, что не все необходимое пространство стека, если оно заранее выделено, только около 12 страниц (48 КБ или 96 КБ). Если критическому коду требуется больше места, он выйдет из строя, и CER не поможет. CER лишь немного повышает гарантию исполнения, но не дает 100% гарантии. - person Pragmateek; 20.10.2013

Переполнение стека - это не то, что вы можете просто восстановить, поскольку он не может выделить больше памяти стека даже для вызова вашего обработчика исключений.

Единственное, что вы действительно можете сделать, - это отследить причину и предотвратить ее вообще (например, будьте осторожны с рекурсией и не размещайте большие объекты в стеке).

person Fire Lancer    schedule 20.09.2008

Blowdart прибил это выше. Рекурсивное свойство тупицы, как он любит это называть. На самом деле проблема с набором кода слишком быстро.

private Thing _myThing = null;

Public Thing MyThing
{
   get{
        return this.MyThing;}
   set{
        this.MyThing = value;}
}
person NBB    schedule 01.06.2010