Важно различать объекты, реализующие IDisposable
, и объекты с финализаторами.В большинстве случаев (вероятно, предпочтительно во всех) объекты с финализаторами также реализуют IDisposable
, но на самом деле это две разные вещи, чаще всего используемые вместе.
Финализатор - это механизм, который сообщает .NET Runtime, чтопрежде чем он сможет собрать объект, он должен выполнить финализатор.Это происходит, когда среда выполнения .NET обнаруживает, что объект пригоден для сборки мусора.Обычно, если у объекта нет финализатора, он будет собран во время этой коллекции.Если у него есть финализатор, он вместо этого будет помещен в список, «свободную очередь», и есть фоновый поток, который отслеживает этот поток.Иногда после того, как коллекция поместила объект в эту очередь, поток финализатора обработает объект из этой очереди и вызовет метод финализатора.
После того, как это произошло, объект снова имеет право на коллекцию, но имееттакже помечен как завершенный, что означает, что когда сборщик мусора находит объект в будущем цикле сбора, он больше не помещает его в эту очередь, а собирает его обычно.
Обратите внимание, что в приведенных выше абзацах текста,IDisposable
не упоминается ни разу, и для этого есть веская причина.Ничто из вышеперечисленного не опирается на IDisposable
вообще .
Теперь объекты, реализующие IDisposable
, могут иметь или не иметь финализатор.Общее правило таково: если сам объект владеет неуправляемыми ресурсами, он, вероятно, должен, а если нет, то, вероятно, не должен. (я не решаюсь говорить всегда и никогда здесь, так как всегда, кажется, кто-то может найти угловой шкаф, в котором это так или иначе имеет смысл, но нарушает "типичное" правило)
A TL; DR Сводка вышесказанного может заключаться в том, что финализатор - это способ (полу) гарантированной очистки объекта, когда он собирается, но именно тогда, когда это происходит, это не напрямую.под контролем программистов, тогда как реализация IDisposable
- это способ управления этой очисткой непосредственно из кода.
В любом случае, со всем этим под нашим поясом, давайте ответим на ваши конкретные вопросы:
Когда приложение C # .NET 4.5 (или выше) закрывается, что происходит с IDisposables, которые не были правильно утилизированы?
Ответ: Ничего.Если у них есть финализатор, поток финализатора попытается их забрать, так как после завершения программы все объекты становятся пригодными для сбора.Тем не менее потоку финализатора не разрешается запускаться "навсегда", чтобы сделать это, поэтому он также может не хватить времени.Если, с другой стороны, объект, реализующий IDisposable
, не имеет финализатора, он будет просто собран обычным образом (опять же, IDisposable
не имеет никакого отношения к сбору мусора).
Нодопустим, у меня есть консольное приложение со статической переменной Stream.Будет ли оно удалено, когда я закрою консольное приложение?
Ответ: Нет, оно не будет утилизировано .Stream
сам по себе является базовым классом, поэтому в зависимости от конкретного производного класса он может иметь или не иметь финализатор.Однако он следует тому же правилу, что и выше, поэтому, если у него нет финализатора, он будет просто собран.Например, MemoryStream не имеет финализатора, тогда как FileStream не имеет.
А как насчет HttpClient?И как вы узнаете, в каких ситуациях это происходит, а в каких нет
Ответ: Справочный источник для HttpClient , кажется, указывает, что HttpClient
не имеет финализатора.Таким образом, он будет просто собран.
Хорошо, теперь некоторая фактическая справочная информация.Я часто храню определенные IDisposables как поля, заставляя мой класс реализовать IDisposable.Конечный пользователь должен использовать, используя.Но что, если этого не произойдет?
Ответ: Если вы забудете / не вызовете IDisposable.Dispose()
для объектов, реализующих IDisposable
, все, что я здесь сказал относительно финализаторов, все равно произойдет, как только объект будет пригоден для сбора.Кроме этого, ничего особенного не произойдет.Реализует ли объект IDisposable
или нет, не имеет никакого отношения к процессу сбора мусора, имеет только наличие финализатора.
Является ли это просто ненужной памятью до GC?Или у вас вдруг произошла утечка памяти
Ответ: Не определено из этой простой информации.Это зависит от того, что будет делать метод Dispose
.Например, если объект где-то зарегистрировал себя, так что где-то есть ссылка на него, так как некоторый код, чтобы прекратить использование объекта, может фактически не сделать объект пригодным для сбора.Метод Dispose
может быть ответственным за его отмену регистрации и удаление последних ссылок на него.Так что это зависит от объекта.Тот факт, что объект реализует IDisposable
, не создает утечку памяти.Если последняя ссылка на объект удаляется, объект становится пригодным для сбора и будет собран в течение будущего цикла сбора.
Примечания:
Обратите внимание, что приведенный выше текст также, вероятно, упрощен.Полный цикл сбора, чтобы фактически «собрать память», вероятно, не выполняется по завершении приложения, так как в этом нет никакого смысла.Операционная система освободит память, выделенную процессом, когда он все равно завершится.Вероятно, выполняется только финализация.(имеется в виду, что я так или иначе не знаю, какие оптимизации здесь проводятся)
Более важной частью здесь является то, что вам необходимо различать утечки памяти (или другие) во время выполнения программы и после выполнения программы
- Когда процесс завершится, операционная система освободит всю выделенную ему память, закроет все дескрипторы (чтоможет держать сокеты, файлы и т. д. открытыми), все потоки будут прерваны.Короче говоря, программа полностью удаляется из памяти
- Хотя процесс мог оставить вокруг себя небольшие кусочки, которые не очищены, если только процесс не позаботился об этом заранее.Открытый файл закрывается, как указано выше, но, возможно, он не был полностью записан и, таким образом, может быть каким-либо образом поврежден.
- Во время выполнения программы из-за утечек программа может расти с точки зрения выделенной памяти.может выделить слишком много дескрипторов, потому что он не может закрыть те, которые ему больше не нужны, и т. д., и это важно с точки зрения правильной обработки
IDisposable
и финализаторов, но когда процесс завершается, это больше не проблема.