Исключения во время Finalize (): какую методологию вы используете для обнаружения исключений времени сборщика мусора? - PullRequest
2 голосов
/ 08 октября 2009

Я занимаюсь разработкой довольно обширной системы в .NET, которая включает в себя много системного программирования. Чаще всего я использую шаблон IDisposable для обработки удаления ресурсов, но иногда это не применимо (или пропущено по ошибке), и ресурс уничтожается во время Finalize (). Это может произойти в COM-взаимодействии или когда деструктор вызывает Dispose (), и внутри него есть исключение.

В основном: не всегда возможно ясно увидеть и обработать каждый сценарий, когда финализатор может бросить. И когда это произойдет, приложение наверняка завершится сбоем.

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

Итак, вопрос для общественности сейчас : если вы отвечаете за подобные проблемы, что вы делаете, чтобы их контролировать? Отметить объекты? Использовать сторонний инструмент, который позволяет отслеживать эти проблемы?

Кроме того: возможно ли вызвать какое-то глобальное событие «бросок финализатора», чтобы хотя бы зарегистрировать, что именно эта проблема произошла?

РЕДАКТИРОВАТЬ1 : Большое спасибо всем, кто представил ценный вклад, я думаю, что теперь я несколько яснее о том, что нужно сделать. Последнее, что я действительно хотел бы получить из этого обсуждения, - это если кто-то знает методологию запуска кода при исключении в финализаторе (даже если сбой приложения все еще неизбежен), чтобы я мог, по крайней мере, регистрировать его без необходимости изменять деструктор каждого класс.

Ответы [ 4 ]

5 голосов
/ 08 октября 2009

Ваш вопрос основан на несколько ошибочной предпосылке, то есть невозможно справиться с каждым сценарием, который может вызвать Финализатор. Это именно то, что вам нужно достичь здесь. Исключения, возникающие во время финализации, убивают процесс. Если исключение не является по-настоящему фатальным, в этом случае позвольте программе аварийно завершить работу, вы должны обрабатывать это исключение таким образом, чтобы ваша программа не аварийно завершалась.

Бросок финализатора почти так же плох, как и бросок деструкторов C ++. Большинство реализаций вызова IDisposable в одном и том же методе для активных (IDisposable.Dispose ()) и пассивных (поток финализатора) операций удаления. Если версия Финализатора выбрасывает, то вполне вероятно, что активная утилизация также может выбросить. Это, подобно метанию деструктора C ++, будет препятствовать правильному расположению вложенных ресурсов в некоторых случаях.

Не позволяйте исключениям распространяться из финализатора, если они не являются действительно фатальными для вашего приложения.

4 голосов
/ 08 октября 2009

Финализаторы нужны только в .NET для классов, которые напрямую владеют неуправляемыми ресурсами (т. Е. Не только через член IDisposable). Такие классы должны реализовывать IDisposable с использованием стандартного шаблона, как описано в MSDN.

Финализатор должен только когда-либо распоряжаться неуправляемыми ресурсами - обычным способом является вызов метода "protected void Dispose (bool dispose)" с аргументом распоряжения, установленным в false.

Этот метод обычно реализован примерно так:

protected void Dispose(bool disposing)
{
    if (disposing)
    {
        // Dispose managed resources here.
        // (e.g. members that implement IDisposable)
        // This could throw an exception, but will *not* be called from the finalizer
        ...

    }
    ... Dispose unmanaged resources here.
    ... no need for any exception handling.
    ... Unlikely to get an exception, and if you do it will be fatal.
}

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

3 голосов
/ 08 октября 2009

В своих классах я делаю следующее, поэтому в отладочных сборках я могу попытаться поймать подобные вещи.

В отладочных сборках делается утверждение, чтобы гарантировать, что класс был явно уничтожен. Еще одно утверждение сделано для того, чтобы финализатор не выдавал. В выпусках выпуска / производства эта проверка не выполняется для повышения производительности.

код C #

using System;
using System.Diagnostics;

namespace ConsoleApplication8
{
    class Program
    {
        class IReferenceUnmanagedMem : IDisposable
        {
#if(DEBUG)
            private static readonly string _ctorStackTrace = Environment.StackTrace;
#endif

            public IReferenceUnmanagedMem()
            {
            }

            ~IReferenceUnmanagedMem()
            {
#if(DEBUG)
                Debug.Fail("Dispose method not called.", _ctorStackTrace);
                try
                {
#endif
                    Dispose(true);
#if(DEBUG)
                }
                catch(Exception e)
                {
                    Debug.Fail("Dispose method threw exception in finalizer.", e.ToString());
                }
#endif
            }

            public void Dispose()
            {
                Dispose(false);
                GC.SuppressFinalize(this);
            }

            protected virtual void Dispose(bool inFinalizer)
            {
                if(inFinalizer)
                {
                    throw new Exception("I Know, this is a no-no.");
                }
            }
        }

        public static void Main()
        {
            IDisposable disposable = new IReferenceUnmanagedMem();
            disposable = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
}
0 голосов
/ 08 октября 2009

Apache DBCP, когда имеет дело с оставленными соединениями, похоже, генерирует исключение при открытии соединения, чтобы сохранить трассировку стека и выплюнуть ее, когда очищает соединение. Для ресурсов, которые не располагаются должным образом, вы можете попробовать использовать такую ​​технику, чтобы отслеживать, как ресурс инициализируется и используется, чтобы метод finalize мог выдавать полезные сообщения об ошибках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...