Выход из приложения C # автоматически распределяет управляемые ресурсы? - PullRequest
0 голосов
/ 04 октября 2018

Я полностью осознаю, что использование операторов - это способ обработки IDisposables.Пожалуйста, не повторяйте этот совет в комментариях.

Когда приложение C # .NET 4.5 (или выше) закрывается, что происходит с IDisposables, которые не были должным образом удалены?

Я знаю, что у некоторых есть финализатор для удаления неуправляемых ресурсов.

Но, скажем, у меня есть консольное приложение со статической переменной Stream.Он удаляется, когда я закрываю консольное приложение?

А как насчет HttpClient?И как вы узнаете, в каких ситуациях это происходит, а в каких нет?

Хорошо, теперь немного реальной информации.Я часто храню определенные IDisposables как поля, заставляя мой класс реализовать IDisposable.Конечный пользователь должен использовать, используя.Но что, если этого не произойдет?

Это просто ненужная память до GC?Или у тебя вдруг произошла утечка памяти?

Ответы [ 4 ]

0 голосов
/ 04 октября 2018

Важно различать объекты, реализующие 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 и финализаторов, но когда процесс завершается, это больше не проблема.
0 голосов
/ 04 октября 2018

Процесс собирается исчезнуть.Это означает, что ОС прекратит предоставлять любую память для поддержки адресного пространства этого процесса 1 .Вся память "исправлена" так или иначе.Это также собирается прекратить все потоки в процессе.Так что никакой дальнейшей обработки не произойдет.Все ручки в любом случае будут закрыты.

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

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

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


1 Процессы не имеют памяти.У них есть адресное пространство, управляемое ОС.Иногда может потребоваться, чтобы некоторые части этого адресного пространства находились в памяти.Это задача ОС в эпоху виртуальной памяти, и она лишь временно «одалживает» физическую память любому процессу.Он может забрать его снова в любое время

0 голосов
/ 04 октября 2018

Ничто не удаляется автоматически, никогда.

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

Нет части CLR, которая вызывает метод Dispose.
ГХ будет собирать ссылку и, если не указано иное (с помощью GC.SuppressFinalize(), затем будет перемещать ссылку в очередь финализатора, где он будет завершен путем вызова метода finalize.
Если и только если класс явно переопределит метод finalize и вызовет Dispose в методе finalize, экземпляр будет окончательно удален.

Итак, если вы хотите, чтобы ваши классы были уничтожены финализатором, вы должны переопределить метод finalize в вашем классе . Однако остерегайтесь - Правильно реализовать метод Finalize сложно!

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

0 голосов
/ 04 октября 2018

При выходе из приложения C # автоматически распределяются управляемые ресурсы?

Ну, да и нет, но технически: нет.

Если интерфейс IDisposable реализован правильно, функция dispose будет вызвана, когда объект будет собран.Если это неправильно реализовано, произойдет утечка, если используются неуправляемые ресурсы.

Когда приложение C # .NET 4.5 (или выше) закрывается, что происходит с IDisposables, которые были неправильноутилизируется?

Пространство процесса освобождено.Если IDisposable неявно резервирует память в других пространствах процессов и неправильно распределяется: у вас есть утечка.

Но, скажем, у меня есть консольное приложение со статической переменной Stream.Удаляется ли оно, когда я закрываю консольное приложение?

Это, хотя и неявно, может привести к неожиданному результату, например, при завершении процесса.Например: порт TCP может находиться в состоянии ожидания.

А как насчет HttpClient?И как вы узнаете, в каких ситуациях это происходит, а в каких нет?

То же, что и выше.

Хорошо, теперь некоторая фактическая справочная информация.Я часто храню определенные IDisposables как поля, заставляя мой класс реализовать IDisposable.Конечный пользователь должен использовать, используя.Но что, если этого не произойдет?

Вы должны реализовать это правильно, но посоветуйте утилизировать.Например, дескриптор файла.Если он не будет правильно удален, другой вызов файла может завершиться ошибкой, поскольку файл все еще открыт.Это может быть решено в GC, но вы не знаете, когда.

Это просто ненужная память до GC?Или у вас внезапно возникает утечка памяти?

Это больше, утечки, открытые порты, открытые дескрипторы, потребление видеопамяти и т. Д.


Для правильной реализации IDisposable см .: Правильное использование интерфейса IDisposable

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

...