Затраты, связанные с деструкторами C # (иначе: финализаторы)? - PullRequest
3 голосов
/ 05 марта 2009

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

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

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

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

Ответы [ 5 ]

8 голосов
/ 05 марта 2009

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

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

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

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

6 голосов
/ 05 марта 2009

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

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

3 голосов
/ 05 марта 2009

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

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

2 голосов
/ 05 марта 2009

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

0 голосов
/ 05 марта 2009

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

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


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

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

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

Резюме

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

image

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