В чем разница между ресурсами, размещенными в финализаторе, и ресурсами, выпущенными в dispose

Это следующий вопрос к этому вопросу:

шаблон Finalize / Dispose в C #

Итак, я понимаю, что если я создаю класс, использующий неуправляемые ресурсы, я должен их удалить. Ответ на связанный вопрос говорит о том, что финализатор избавляется от неуправляемых ресурсов. Однако метод Dispose(Boolean) также избавляется от неуправляемых ресурсов:

protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    } 

Так в чем же разница между удалением финализатора и удалением метода удаления?


person Yonatan Nir    schedule 22.01.2019    source источник
comment
Как вы можете видеть в принятом ответе, финализатор - если он существует. - обычно вызывает ваш Dispose-метод с флагом false.   -  person HimBromBeere    schedule 22.01.2019
comment
Это сборщик мусора, который собирает мусор и вызывает финализатор. Обратите внимание, что нет никакой гарантии, что мусор будет вообще собран (или, если это будет, когда это будет сделано). Напротив, Dispose вызывается вручную (обычно при выходе из области using).   -  person Dmitry Bychenko    schedule 22.01.2019
comment
Значит, финализатор существует ТОЛЬКО для вызова метода Dispose в случае, если пользователь забыл вызвать его сам?   -  person Yonatan Nir    schedule 22.01.2019
comment
Да, финализатор существует для обеспечения высвобождения неуправляемых ресурсов, когда это возможно. Невыпущенный неуправляемый ресурс может вызвать хаос - представьте, что дескриптор файла неправильно оставлен открытым на время существования приложения.   -  person Matthew Watson    schedule 22.01.2019
comment
Я понимаю последствия. Я просто подумал, что и Dispose, и финализатор делают что-то вроде Release (unmanagedObject), и не понимал почему. но если реализация финализатора ВСЕГДА в случае этого шаблона будет Dispose (false), то я это понимаю. Просто чтобы подтвердить - это правильно?   -  person Yonatan Nir    schedule 22.01.2019
comment
Ключевым моментом здесь является то, что финализатор вызывается только в том случае, если метод Dispose () не вызывается (или метод Dispose () забывает вызвать GC.SuppressFinalize(this)). Таким образом, финализатор должен каким-то образом избавиться от неуправляемых ресурсов, а Dispose () должен избавиться как от неуправляемых, так и от управляемых ресурсов. Это то, что покрывает шаблон Dispose.   -  person Matthew Watson    schedule 22.01.2019
comment
что означает, что действительно в случае шаблона удаления реализация финализатора ВСЕГДА будет просто одной строкой Dispose (false)?   -  person Yonatan Nir    schedule 22.01.2019
comment
Да, если вы следуете шаблону Dispose (), это будет правдой.   -  person Matthew Watson    schedule 22.01.2019


Ответы (3)


Единственная причина, по которой вы его использовали (и это крайне спорно).

  1. Финализатор позволяет очистить объект до того, как он будет удален сборщиком мусора. (То есть сборщик мусора отвечает за его вызов и очистку объекта из памяти). Если разработчик забыл вызвать Dispose() метод объекта, то можно будет освободить неуправляемые ресурсы и, таким образом, избежать утечки.

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

person TheGeneral    schedule 22.01.2019
comment
Финализатор позволяет очистить объект до того, как он будет удален сборщиком мусора. Разве финализатор не вызывается сборщиком мусора? - person HimBromBeere; 22.01.2019
comment
@HimBromBeere GC отвечает за его вызов и очистку объекта из памяти. - person TheGeneral; 22.01.2019
comment
И первая причина из 15 не делать это то, что освобождение ресурса в финализаторе усугубляет ситуацию с утечкой ресурсов: он становится нестабильным. - person Dmitry Bychenko; 22.01.2019
comment
@DmitryBychenko именно так, это выбор вашего собственного приключения, как потенциально сделать что-то неприятное - person TheGeneral; 22.01.2019
comment
Когда GC очищает объект, у которого есть метод finalize, будет ли он просто запускать метод finalize, предполагая, что метод также очистит управляемые ресурсы, или он освободит сами управляемые ресурсы, предполагая, что финализатор освобождает ТОЛЬКО неуправляемые ресурсы? - person Yonatan Nir; 22.01.2019
comment
Финализатор никогда не должен касаться управляемых ресурсов. Это одно из правил использования финализатора. Вот почему в шаблоне Dispose используется флаг для защиты управляемых ресурсов. - person pinkfloydx33; 22.01.2019
comment
@ pinkfloydx33 действительно, к сожалению, я написал это на своем телефоне и потерял желание добавлять больше деталей. Короче говоря, на самом деле нет веских причин использовать их, чтобы весить плохие - person TheGeneral; 22.01.2019
comment
@TheGeneral извините, это был комментарий к OP - person pinkfloydx33; 22.01.2019

В дополнение к данному ответу: финализатор вызывается сборщиком мусора при его запуске.

Таким образом, вы не можете рассчитывать на время высвобождения неуправляемых ресурсов в финализаторе! Потому что неизвестно.

Кроме того, финализатор работает в другом потоке, поэтому после завершения сборки мусора финализация может продолжаться! Таким образом, чтобы полностью избавиться от объекта, необходимо выполнить другую сборку мусора.

Итак, первая сборка мусора вызывает finalezrs, но объект не собирается (а также объекты, на которые объект содержит ссылки), он будет собран при второй сборке мусора.

person Michał Turczyn    schedule 22.01.2019
comment
Когда GC очищает объект, у которого есть метод finalize, будет ли он просто запускать метод finalize, предполагая, что метод также очистит управляемые ресурсы, или он освободит сами управляемые ресурсы, предполагая, что финализатор освобождает ТОЛЬКО неуправляемые ресурсы? - person Yonatan Nir; 22.01.2019
comment
@YonatanNir Нет, вам нужно написать код для освобождения неуправляемых ресурсов. Вы можете сделать это в Dispose методе или в финализаторе. Я предпочитаю метод Dispose, потому что он запускается сразу после его вызова. Финализатор вызывается сборщиком мусора, время которого неизвестно. - person Michał Turczyn; 22.01.2019
comment
Я не об этом спрашивал. Я спросил, вызовет ли сборщик мусора финализатор и освободит его из памяти, или сборщик мусора вызовет ТОЛЬКО финализатор, потому что сборщик мусора предполагает, что финализатор также позаботится об управляемых ресурсах? - person Yonatan Nir; 22.01.2019
comment
@YonatanNir GC будет вызывать финализатор только при первой сборке мусора. Финализатор вызывается в другом потоке, поэтому сборка мусора завершится, не дожидаясь вызова финализаторов. Память будет освобождена при второй сборке мусора - person Michał Turczyn; 22.01.2019
comment
Турчин: Ладно, в конце концов, он сделает и то, и другое, но не одновременно. Просто из любопытства, как сборщик мусора может узнать, что финализатор завершил работу, даже если он ждал второй раз? - person Yonatan Nir; 22.01.2019
comment
@YonatanNir Ну, я точно не знаю. Но я знаю, что вы можете вызвать метод: GC.WaitForPendingFinalizers(), чтобы убедиться, что все финализаторы завершены. Документация - person Michał Turczyn; 22.01.2019
comment
И последний вопрос ... В случае вызова Dispose (true) - как именно вы избавляетесь от управляемых ресурсов? Разве GC не предполагает этого? - person Yonatan Nir; 22.01.2019
comment
@YonatanNir Нет. В этом весь смысл реализации Dispose метода. Предположим, у вашего класса есть соединение с базой данных: SqlConnection conn;, где-то вы его открываете, но вам также нужно закрыть его, когда вы избавляете от объекта. Затем в вашем Dispose методе вам нужно вызвать conn.Close() метод, чтобы освободить этот неуправляемый ресурс. - person Michał Turczyn; 22.01.2019
comment
Теперь я говорил об УПРАВЛЯЕМЫХ ресурсах. Шаблон удаления указывает, что вы должны переопределить Dispose (Boolean). Логическое значение, если оно истинно, означает удаление управляемых ресурсов (например, фрагмента кода в моем вопросе). Итак, если они управляются, не сделает ли это сборщик мусора? - person Yonatan Nir; 22.01.2019
comment
@YonatanNir Ах, сборщик мусора избавится от управляемых ресурсов, а не Dispose метод. - person Michał Turczyn; 22.01.2019
comment
Опять же ... что должно произойти, если Dispose вызывается со значением true? во фрагменте говорится, что необходимо избавиться от УПРАВЛЯЕМЫХ ресурсов .. - person Yonatan Nir; 22.01.2019
comment
@YonatanNir Нет, идея метода Dispose не в этом. Предполагается, что он будет управлять только незащищенными ресурсами. Об управляемых ресурсах заботится сборщик мусора. - person Michał Turczyn; 22.01.2019
comment
Итак, почему у метода dispose есть логический аргумент? Как вы думаете, что должно произойти в случае истинного значения или в случае ложного значения? - person Yonatan Nir; 22.01.2019
comment
@YonatanNir Все необходимые ответы можно найти здесь: docs. microsoft.com/pl-pl/dotnet/standard/garbage-collection/ Мне тоже нужно прочитать это - очень полезный ресурс по этой теме :) - person Michał Turczyn; 22.01.2019

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

Вот почему реализация шаблона Dispose всегда включает вызов GC.SuppressFinalize, из-за которого финализатор не запускается в случае, если объект уже был удален, и сборщик мусора может напрямую освободить память при первом запуске.

Как правило, использование финализаторов может быть очень сложным и запутанным, если ваше приложение должно пережить критические исключения, такие как нехватка памяти или прерывание потока и последующие выгрузки AppDomain - это случай таких приложений, как SQL Server или IIS.

Короче говоря: не используйте финализаторы, если в этом нет крайней необходимости, и если вам нужно (например, с использованием неуправляемых ресурсов), вас ждет довольно много исследований.

Вы можете найти больше информации по этой теме в следующих сообщениях блога:

Эрик Липперт - Когда все, что ты знаешь неправильно

Джо Даффи - Никогда не пишите финализатор снова

person Zdeněk Jelínek    schedule 22.01.2019
comment
Основная идея этого ответа хороша, но просто чтобы прояснить ваше первое предложение: во-первых, поток GC определяет, что объект является финализируемым мусором, и объект помещается в очередь финализатора, которая является корнем GC. Затем, после запуска потока финализатора, объект обычно отмечает себя как не подлежащий финализации. Затем более поздний сборщик мусора идентифицирует объект как незавершенный мусор, и эта память становится дырой в куче сборщика мусора, которая сжимается. - person Eric Lippert; 22.01.2019
comment
То, как вы это сформулировали, звучит так, как будто поток GC запускает финализаторы, но это не так. Он просто помещает объекты в очередь, чтобы поток финализатора обрабатывал их позже. - person Eric Lippert; 22.01.2019