Затраты, связанные с деструкторами C # (они же:финализаторы)?

StackOverflow https://stackoverflow.com/questions/613290

Вопрос

Деструктор должен освобождать только неуправляемые ресурсы, к которым привязан ваш объект, и он не должен ссылаться на другие объекты.Если у вас есть только управляемые ссылки, вам не нужно (и не следует) внедрять деструктор.Это нужно только для обработки неуправляемых ресурсов. Потому что наличие деструктора сопряжено с определенными затратами, вы должны реализовать это только на методах, которые потребляют ценные, неуправляемые ресурсы.

-- Десять лучших ловушек в C # для программистов на C ++

В статье не рассматривается этот вопрос более подробно, но какие затраты связаны с использованием деструктора в C #?

Примечание: Я знаю о GC и о том факте, что деструктор не вызывается в надежное время, это все в стороне, есть ли что-нибудь еще?

Это было полезно?

Решение

Любой объект, имеющий финализатор (я предпочитаю этот термин деструктору, чтобы подчеркнуть отличие от деструкторов C ++), добавляется в очередь финализатора. Это список ссылок на объекты, которые имеют финализатор, который должен быть вызван до того, как они будут удалены.

Когда объект собирается на сборку мусора, GC обнаружит, что он находится в очереди финализатора, и переместит ссылку на свободную (f-достижимую) очередь. Это список, через который проходит фоновый поток финализатора для вызова метода финализатора каждого объекта по очереди.

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

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

Другие советы

Самое обширное обсуждение, которое я видел, как все это было сделано Джо Даффи . В нем больше деталей, чем вы можете себе представить.

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

Guffa и JaredPar довольно подробно освещают детали, поэтому я просто добавлю несколько эзотерических заметок о финализаторах или деструкторах, как, к сожалению, называется в спецификации языка C #.

Следует иметь в виду, что поскольку поток финализатора последовательно запускает все финализаторы, взаимоблокировка в финализаторе препятствует запуску всех оставшихся (и будущих) финализаторов. Поскольку эти экземпляры не собираются до тех пор, пока их финализаторы не завершат работу, заблокированный финализатор также вызовет утечку памяти.

Эта статья подробно описывает проблему. Подвести итоги в простом SO сообщении очень сложно: http: // msdn. microsoft.com/en-us/magazine/bb985010.aspx

Гуффа довольно хорошо суммировал факторы, влияющие на стоимость финализатора.Недавно произошел один Статья о стоимости финализаторов в Java, что также дает некоторое представление.

Части затрат в .net можно избежать, удалив объект из очереди финализатора с помощью GC.SuppressFinalize .Я провел несколько быстрых тестов в .net на основе статьи и опубликовал ее здесь (хотя основное внимание уделяется гораздо больше Java-стороне).


Ниже приведен график результатов - на самом деле у него не самые лучшие метки ;-)."Debug=true/ false" относится к пустому финализатору vs simple:

~ConditionalFinalizer()  
{  
    if (DEBUG)  
    {  
        if (!resourceClosed)  
        {  
            Console.Error.WriteLine("Object not disposed");  
        }  
        resourceClosed = true;  
    }  
} 

"Suppress=true" указывает, был ли вызван GC.SuppressFinalize в методе Dipose.

Краткие сведения

Для .net удаление объекта из очереди финализатора путем вызова GC.SuppressFinalize составляет половину стоимости оставления объекта в очереди.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top