Не гарантировано ли уничтожение деструкторов?

Деструкторы странные. Я пытался устранить необходимость использования одноразового шаблона с помощью "умного" управления ссылками, гарантируя, что сборщик мусора может собирать объекты в нужное время. В одном из моих деструкторов мне пришлось ждать события от другого объекта, и я заметил, что это не так. Приложение просто отключилось, а деструктор был прерван в середине исполнения. Я ожидаю, что деструктор всегда разрешен для завершения, но, как показывает следующий тест, это неверно.

using System;
using System.Diagnostics;
using System.Threading;


namespace DestructorTest
{
    class Program
    {
        static void Main( string[] args )
        {
            new DestructorTest();
            new LoopDestructorTest();
            using ( new DisposableTest() ) { }
        }
    }

    class DestructorTest
    {
        ~DestructorTest()
        {
            // This isn't allowed to finish.
            Thread.Sleep( 10000 );
        }       
    }

    class LoopDestructorTest
    {
        ~LoopDestructorTest()
        {           
            int cur = 0;
            for ( int i = 0; i < int.MaxValue; ++i )
            {
                cur = i;
            }
            // This isn't allowed to finish.
            Debug.WriteLine( cur );
        }
    }

    class DisposableTest : IDisposable
    {
        public void Dispose()
        {
            // This of course, is allowed to finish.
            Thread.Sleep( 10000 );
        }
    }
}

Итак, не являются деструкторами, гарантирующими завершение работы?

Ответ 1

Итак, не уничтожены ли деструкторы?

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

Вы должны рассматривать финализацию как "лучшую попытку" - в частности, это не произойдет в ситуациях, когда вся система внезапно отключается, например, BSOD или отключение питания.

EDIT: я нашел некоторую псевдо-документацию в виде сообщения от Джо Даффи:

Если блокировка осталась сиротой в процессе остановки всех запущенных потоков, тогда путь кода завершения работы не сможет получить блокировку. Если эти приобретения будут выполнены с использованием таймаута (или длительного таймаута), произойдет зависание. Чтобы справиться с этим (и любой другой вид зависания, который может произойти), CLR назначает сторожевую нить, чтобы следить за потоком финализатора. Хотя конфигурация, по умолчанию, CLR позволяет финализаторам работать в течение 2 секунд, прежде чем стать нетерпеливым; если этот тайм-аут превышен, поток финализатора остановлен, и выключение продолжается, не опуская оставшуюся часть очереди финализатора.

Ответ 2

Итак, не уничтожены ли деструкторы?

Хотя его не в вашем коде могут быть экземпляры, где GC.SuppressFinalize явно вызывается из метода Dispose(). Это подавляет завершение и предназначено для объектов, которые этого не требуют.

Это может значительно повысить производительность, поскольку финализированные объекты всегда будут выживать в одной сборке мусора, т.е. она будет повышена до Gen1 или даже GC Gen2, которая имеет большую стоимость, связанную с ней.

Ответ 3

.NET не поставляется с деструкторами. Вместо этого ваш код содержит финализаторы.

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

См. также https://en.wikipedia.org/wiki/Finalizer.

Ответ 4

Еще один способ взглянуть на то, что финализаторы вызывают, когда сборщик мусора освобождает память.

Однако, если у вас была программа, которая в большинстве случаев требовала 1 Мб памяти, но работала на машине с 10 Мб памяти, тогда действительная реализация сборщика грабежа была бы ничего не делать (поскольку для программы было бы достаточно памяти для выполнение). В этом случае финализаторы никогда не будут называться.