Я знаю из чтения документации MSDN, что "основное" использование интерфейса IDisposable
- очистка неуправляемых ресурсов.
Для меня "неуправляемый" означает такие вещи, как соединения с базой данных, сокеты, дескрипторы окон и т.д. Но я видел код, в котором метод Dispose()
реализован для освобождения управляемых ресурсов, что кажется излишним для меня, поскольку сборщик мусора должен позаботиться об этом для вас.
Например:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Мой вопрос в том, делает ли свободную память сборщика мусора, используемую MyCollection
, быстрее, чем обычно?
edit. До сих пор люди опубликовали несколько хороших примеров использования IDisposable для очистки неуправляемых ресурсов, таких как соединения с базой данных и растровые изображения. Но предположим, что _theList
в приведенном выше коде содержал миллион строк, и вы хотели освободить эту память сейчас, а не ждать сборщика мусора. Может ли этот код выполнить этот код?
Точка Dispose -, чтобы освободить неуправляемые ресурсы. Это нужно сделать в какой-то момент, иначе они никогда не будут очищены. Сборщик мусора не знает как для вызова DeleteHandle()
для переменной типа IntPtr
, он не знает , или не нужно вызвать DeleteHandle()
.
Примечание. Что такое неуправляемый ресурс? Если вы нашли его в Microsoft.NET Framework: он управляется. Если вы сами потрудились над MSDN, это неуправляемо. Все, что вы использовали P/Invoke, чтобы выйти за пределы приятного удобного мира всего, что вам доступно в .NET Framwork, неуправляемо - и теперь вы отвечаете за его очистку.
Созданный объект должен выставить метод some, который может вызвать внешний мир, чтобы очистить неуправляемые ресурсы. Метод можно назвать любым, что вам нравится:
public void Cleanup()
public void Shutdown()
Код>
Но вместо этого существует стандартизованное имя для этого метода:
public void Dispose()
Код>
Был создан даже интерфейс, IDisposable
, который имеет только один метод:
открытый интерфейс IDisposable
{ void Dispose()
}
Код>
Таким образом, вы создаете свой объект для интерфейса IDisposable
, и таким образом вы обещаете, что вы написали этот единственный метод для очистки неуправляемых ресурсов:
public void Dispose()
{ Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
Код>
И все готово. Кроме того, что вы можете сделать лучше.
Что делать, если ваш объект выделил 250 МБ Система .Drawing.Bitmap (т.е. Управляемый .NET класс Bitmap) в качестве своего рода буфера кадров? Конечно, это управляемый объект .NET, и сборщик мусора освободит его. Но вы действительно хотите оставить 250MB памяти, просто сидите там, ожидая, когда сборщик мусора в в конце концов придет и освободит его? Что, если есть открытие подключения к базе данных? Конечно, мы не хотим, чтобы это соединение закрывалось, ожидая завершения GC GC.
Если пользователь вызвал Dispose()
(что означает, что они больше не планируют использовать объект), почему бы не избавиться от этих расточительных растровых изображений и соединений с базой данных?
Итак, теперь мы будем:
Итак, давайте обновим наш метод Dispose()
, чтобы избавиться от этих управляемых объектов:
public void Dispose()
{ // Свободные неуправляемые ресурсы Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
// Свободные управляемые ресурсы тоже if (this.databaseConnection!= null) { this.databaseConnection.Dispose(); this.databaseConnection = null; } if (this.frameBufferImage!= null) { this.frameBufferImage.Dispose(); this.frameBufferImage = null; }
}
Код>
И все хорошо, за исключением того, что вы можете сделать лучше!
Что делать, если человек забыл вызвать Dispose()
на вашем объекте? Затем они будут утечка некоторых неуправляемых ресурсов!
Примечание. Они не будут утечка управляемых ресурсов, потому что в конечном итоге сборщик мусора будет запускаться в фоновом потоке и освободить память, связанную с любыми неиспользуемыми объекты. Это будет включать в себя ваш объект и любые управляемые объекты, которые вы используете (например,
Bitmap
иDbConnection
).
Если человек забыл вызвать Dispose()
, мы можем еще сохранить свой бекон! У нас все еще есть способ называть его для: когда сборщик мусора наконец-то обходит освобождение (то есть завершение) нашего объекта.
Примечание. Сборщик мусора в конечном итоге освободит все управляемые объекты. Когда это произойдет, он вызывает
Finalize
метод на объекте. ГК не знает, или Disposeметод. Это было просто имя, которое мы выбрали для метод, который мы называем, когда хотим избавиться от неуправляемого материала.
Разрушение нашего объекта сборщиком мусора - это perfect время, чтобы освободить эти надоедливые неуправляемые ресурсы. Мы делаем это, переопределяя метод Finalize()
.
<Предварительно > <код > ~ MyObject() { // мы завершаем (т.е. уничтожен), вызываем Dispose в случае, если пользователь забыл Dispose();//< - Предупреждение: тонкая ошибка! Продолжай читать! } Код >Примечание.. В С# вы явно не переопределяете метод
Finalize()
. Вы пишете метод, который выглядит как деструктор С++, а компилятор считает, что это ваша реализация методаFinalize()
:
Но в этом коде есть ошибка. Видите ли, сборщик мусора работает в фоновом потоке; вы не знаете порядок, в котором уничтожаются два объекта. Вполне возможно, что в коде Dispose()
объект управляемого, который вы пытаетесь избавиться (потому что вы хотели быть полезным) больше не существует:
public void Dispose()
{ // Свободные неуправляемые ресурсы Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
// Свободные управляемые ресурсы тоже if (this.databaseConnection!= null) { this.databaseConnection.Dispose();//< - crash, GC уже уничтожил его this.databaseConnection = null; } if (this.frameBufferImage!= null) { this.frameBufferImage.Dispose();//< - crash, GC уже уничтожил его this.frameBufferImage = null; }
}
Код>
Итак, для того, чтобы Finalize()
указать Dispose()
, чтобы он не касался каких-либо управляемых ресурсов, они могут быть не там), но при этом освобождают неуправляемые ресурсы.
Стандартный шаблон для этого состоит в том, чтобы Finalize()
и Dispose()
вызывать метод третий (!); где вы передаете логическое высказывание, если вы вызываете его из Dispose()
(в отличие от Finalize()
), то есть безопасно освобождать управляемые ресурсы.
В этом внутреннем методе может задано какое-то произвольное имя, например "CoreDispose" или "MyInternalDispose", но традиционно называют его Dispose (Boolean)код>:
protected void Dispose (Boolean disposing)
Код>
Но более полезным именем параметра может быть:
protected void Dispose (Boolean itIsSafeToAlsoFreeManagedObjects)
{ // Свободные неуправляемые ресурсы Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
// Свободные управляемые ресурсы тоже, но только если меня вызывают из Dispose // (Если я вызывается из Finalize, тогда объекты могут не существовать // больше if (itIsSafeToAlsoFreeManagedObjects) { if (this.databaseConnection!= null) { this.databaseConnection.Dispose(); this.databaseConnection = null; } if (this.frameBufferImage!= null) { this.frameBufferImage.Dispose(); this.frameBufferImage = null; } }
}
Код>
И вы измените свою реализацию метода IDisposable.Dispose()
на:
public void Dispose()
{ Dispose (истина);//Я звоню вам из Dispose, это безопасно
}
Код>
и ваш финализатор:
<Предварительно > <код > ~ MyObject() { Dispose (ложь);//Я * не * вызываю вас из Dispose, это * не * безопасно } Код >Примечание. Если ваш объект сходит с объекта, который реализует
Dispose
, то не забудьте вызвать их метод base Dispose, когда вы override Dispose:
public Dispose()
{ пытаться { Dispose (истина);//true: безопасные свободные управляемые ресурсы } в конце концов { base.Dispose(); }
}
Код>
И все хорошо, за исключением того, что вы можете сделать лучше!
Если пользователь вызывает Dispose()
на вашем объекте, тогда все было очищено. Позже, когда сборщик мусора появится и вызовет Finalize, он снова вызовет Dispose
.
Не только это расточительно, но если у вашего объекта есть ненужные ссылки на объекты, которые вы уже удалили из вызова last на Dispose()
, вы попытаетесь уничтожить их снова!
В моем коде вы заметите, что я старался удалить ссылки на объекты, которые я выбрал, поэтому я не пытаюсь вызвать Dispose
в ссылке на нежелательные объекты. Но это не остановило тонкую ошибку от ползания.
Когда пользователь вызывает Dispose()
: уничтожается дескриптор CursorFileBitmapIconServiceHandle. Позже, когда работает сборщик мусора, он попытается снова уничтожить тот же дескриптор.
protected void Dispose (Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{ // Свободные неуправляемые ресурсы Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);//< - double destroy ...
}
Код>
Как вы это исправите, расскажите сборщику мусора, что ему не нужно беспокоиться о завершении объекта - его ресурсы уже очищены, и больше не требуется работать. Вы делаете это, вызывая GC.SuppressFinalize()
в методе Dispose()
:
public void Dispose()
{ Dispose (истина);//Я звоню вам из Dispose, это безопасно GC.SuppressFinalize(это);//Привет, GC: не надо звонить позже
}
Код>
Теперь, когда пользователь вызвал Dispose()
, мы имеем:
Нет смысла в GC запускать финализатор - все позаботится.
Документация для Object.Finalize
говорит:
Метод Finalize используется для выполнения операций очистки неуправляемых ресурсов, удерживаемых текущим объектом до уничтожения объекта.
Но в документации MSDN также говорится, что для <код > IDisposable.Disposeкод > :
Выполняет задачи, связанные с приложением, связанные с освобождением, выпуском или сбросом неуправляемых ресурсов.
Итак, что это? Какой из них является местом для меня, чтобы очистить неуправляемые ресурсы? Ответ:
Это ваш выбор! Но выберите
Dispose
.
Конечно, вы можете разместить неуправляемую очистку в финализаторе:
<Предварительно > <код > ~ MyObject() { // Свободные неуправляемые ресурсы Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); // Деструктор С# автоматически вызывает деструктор своего базового класса. } Код >Проблема с этим - вы понятия не имеете, когда сборщик мусора обойдется до завершения вашего объекта. Ваши неуправляемые, ненужные, неиспользуемые собственные ресурсы будут храниться до тех пор, пока не будет запущен сборщик мусора в конечном итоге. Затем он вызовет ваш метод финализатора; очистка неуправляемых ресурсов. В документации Object.Finalize указано следующее:
Точное время выполнения финализатора - undefined. Чтобы обеспечить детерминированное выделение ресурсов для экземпляров вашего класса, выполните метод Закрыть или укажите
IDisposable.Dispose
.
Это преимущество использования Dispose
для очистки неуправляемых ресурсов; вы узнаете и контролируете, когда неуправляемый ресурс очищается. Их разрушение "детерминировано" .
Чтобы ответить на ваш первоначальный вопрос: почему бы не освободить память сейчас, а не когда GC решит это сделать? У меня есть программное обеспечение для распознавания лиц, которое нуждается в, чтобы избавиться от 530 МБ внутренних изображений сейчас, так как они больше не нужны. Когда мы этого не сделаем: машина перемалывается до замены.
Для тех, кто любит стиль этого ответа (объясняя why, поэтому how становится очевидным), я предлагаю вам прочитать главу 1 из Essential COM от Don Box:
На 35 страницах он объясняет проблемы использования двоичных объектов и изобретает COM перед вашими глазами. После того, как вы осознаете why COM, оставшиеся 300 страниц очевидны и просто детализируют реализацию Microsoft.
Я думаю, что каждый программист, который когда-либо занимался объектами или COM, должен, по крайней мере, прочитать первую главу. Это лучшее объяснение чего-либо когда-либо.
Когда все, что вы знаете, неправильно Эрик Липперт
Поэтому очень сложно написать правильный финализатор, и лучший совет, который я могу вам дать, - не попробовать.
IDisposable
часто используется для использования оператора using
и использует простой способ сделать детерминированную очистку управляемых объектов.
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
Цель шаблона Dispose - предоставить механизм для очистки как управляемых, так и неуправляемых ресурсов, и когда это произойдет, зависит от способа вызова метода Dispose. В вашем примере использование Dispose на самом деле не делает ничего, что связано с удалением, так как очистка списка не влияет на размещение этой коллекции. Аналогично, вызовы для установки переменных в null также не влияют на GC.
Вы можете ознакомиться с этой статьей для более подробной информации о том, как реализовать шаблон Dispose, но в основном выглядит так:
public class SimpleCleanup : IDisposable
{
// some fields that require cleanup
private SafeHandle handle;
private bool disposed = false; // to detect redundant calls
public SimpleCleanup()
{
this.handle = /*...*/;
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources.
if (handle != null)
{
handle.Dispose();
}
}
// Dispose unmanaged managed resources.
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Наиболее важным здесь является Dispose (bool), который фактически работает в двух разных обстоятельствах:
Проблема простое, чтобы GC заботиться о том, чтобы очистить, состоит в том, что у вас нет реального контроля, когда GC будет запускать цикл сбора (вы можете вызвать GC.Collect(), но вы действительно не должны этого делать) ресурсы могут оставаться дольше, чем необходимо. Помните, что вызов Dispose() фактически не вызывает цикл сбора или каким-либо образом заставляет GC собирать/освобождать объект; он просто предоставляет средства для более детерминированной очистки используемых ресурсов и сообщает GC, что эта очистка уже выполнена.
Весь смысл IDisposable и шаблона dispose заключается не в немедленном освобождении памяти. Единственный раз, когда вызов Dispose на самом деле даже имеет шанс немедленно освободить память, когда он обрабатывает сценарий dispose == false и манипулирует неуправляемыми ресурсами. Для управляемого кода память фактически не будет восстановлена до тех пор, пока GC не проведет цикл сбора, который вы действительно не контролируете (кроме вызова GC.Collect(), о котором я уже говорил, это не очень хорошая идея).
Ваш сценарий действительно недействителен, поскольку строки в .NET не используют какие-либо неизведанные ресурсы и не реализуют IDisposable, нет способа заставить их "очищаться".
Не должно быть никаких дальнейших вызовов объектных методов после вызова Dispose (хотя объект должен терпеть дальнейшие вызовы Dispose). Поэтому пример в вопросе глупо. Если Dispose вызывается, то сам объект может быть отброшен. Таким образом, пользователь должен просто отбросить все ссылки на весь этот объект (установить их в null), и все связанные с ним объекты будут автоматически очищены.
Что касается общего вопроса об управляемом/неуправляемом и обсуждении в других ответах, я думаю, что любой ответ на этот вопрос должен начинаться с определения неуправляемого ресурса.
Что сводится к тому, что есть функция, которую вы можете вызвать, чтобы привести систему в состояние, и есть еще одна функция, которую вы можете вызвать, чтобы вывести ее из этого состояния. Теперь, в типичном примере, первая может быть функцией, которая возвращает дескриптор файла, а второй - вызовом CloseHandle
.
Но - и это ключ - они могут быть любой подходящей парой функций. Один строит состояние, другой срывает его. Если государство было построено, но не снесено, то существует экземпляр ресурса. Вам необходимо организовать разрывы в нужное время - ресурс не управляется CLR. Единственным автоматически управляемым типом ресурса является память. Существует два вида: GC и стек. Типы значений управляются стеком (или путем приведения в движение внутри ссылочных типов), а ссылочные типы управляются GC.
Эти функции могут приводить к изменениям состояния, которые могут свободно перемежаться или, возможно, должны быть полностью вложенными. Изменения состояния могут быть потокобезопасными, или они могут не быть.
Посмотрите на пример в вопросе "Правосудие". Изменения в отладке файла журнала должны быть полностью вложенными, или все идет не так. Также они вряд ли будут потокобезопасными.
Можно собрать поездку с сборщиком мусора, чтобы очистить неуправляемые ресурсы. Но только если функции изменения состояния являются потокобезопасными, а два состояния могут иметь периоды жизни, которые перекрываются каким-либо образом. Итак, пример юстиции ресурса НЕ должен иметь финализатор! Это никому не помогло.
Для этих ресурсов вы можете просто реализовать IDisposable
без финализатора. Финализатор абсолютно необязателен - это должно быть. Это замалчивается или даже не упоминается во многих книгах.
Затем вам нужно использовать оператор using
, чтобы иметь возможность гарантировать, что вызывается Dispose
. Это по существу похоже на то, что вы едете со стеком (так как финализатор относится к GC, using
относится к стеку).
Недопустимая часть состоит в том, что вам нужно вручную записать Dispose и заставить ее вызывать свои поля и ваш базовый класс. Программистам С++/CLI этого не нужно. В большинстве случаев компилятор записывает их для них.
Есть альтернатива, которую я предпочитаю для состояний, которые идеально встраиваются и не являются потокобезопасными (кроме всего прочего, избегая IDisposable spares, вы имеете проблему с аргументом с кем-то, кто не может удержаться от добавления финализатора для каждого класса, который реализует IDisposable).
Вместо написания класса вы пишете функцию. Функция принимает делегата для обратного вызова:
public static void Indented(this Log log, Action action)
{
log.Indent();
try
{
action();
}
finally
{
log.Outdent();
}
}
И тогда простой пример:
Log.Write("Message at the top");
Log.Indented(() =>
{
Log.Write("And this is indented");
Log.Indented(() =>
{
Log.Write("This is even more indented");
});
});
Log.Write("Back at the outermost level again");
Пропускаемая лямбда служит в качестве кодового блока, поэтому, как и вы, ваша собственная структура управления служит той же цели, что и using
, за исключением того, что у вас больше нет опасности, что вызывающий абонент злоупотребляет ею. Нет способа, чтобы они не смогли очистить ресурс.
Этот метод менее полезен, если ресурс является видом, который может иметь перекрывающиеся сроки жизни, потому что тогда вы хотите иметь возможность создавать ресурс A, затем ресурс B, а затем убивать ресурс A, а затем убивать ресурс B. Вы можете ' t сделать это, если вы заставили пользователя идеально вложить такие гнезда. Но тогда вам нужно использовать IDisposable
(но все же без финализатора, если только вы не реализовали потоки, которые не являются бесплатными).
Сценарии Я использую IDisposable: очистка неуправляемых ресурсов, отмена подписки на события, закрытые соединения
Идиома, которую я использую для реализации IDisposable (not threadsafe):
class MyClass : IDisposable {
// ...
#region IDisposable Members and Helpers
private bool disposed = false;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) {
if (!this.disposed) {
if (disposing) {
// cleanup code goes here
}
disposed = true;
}
}
~MyClass() {
Dispose(false);
}
#endregion
}
Если MyCollection
собирается собирать мусор в любом случае, вам не нужно его утилизировать. Это приведет к простому откату CPU больше, чем необходимо, и может даже аннулировать некоторый предварительно вычисленный анализ, который уже сделал сборщик мусора.
Я использую IDisposable
для выполнения таких действий, как обеспечение корректного удаления потоков, а также неуправляемых ресурсов.
EDIT В ответ на комментарий Скотта:
Единственный момент, когда влияют показатели производительности GC, - это когда вызывается [sic] GC.Collect() "
Концептуально, GC поддерживает представление о графе ссылок на объекты и все ссылки на него из фреймов стека потоков. Эта куча может быть довольно большой и охватывать многие страницы памяти. В качестве оптимизации GC анализирует страницы, которые вряд ли будут меняться очень часто, чтобы избежать повторного сканирования страницы без необходимости. GC получает уведомление от ядра, когда данные на странице меняются, поэтому он знает, что страница загрязнена и требует повторного сканирования. Если коллекция находится в Gen0, то вероятно, что другие вещи на странице тоже меняются, но это менее вероятно в Gen1 и Gen2. Анекдотически эти крючки не были доступны в Mac OS X для команды, которая портировала GC на Mac, чтобы получить подключаемый модуль Silverlight, работающий на этой платформе.
Еще один момент против ненужной утилизации ресурсов: представьте ситуацию, когда процесс выгружается. Представьте также, что этот процесс работает некоторое время. Скорее всего, многие из этих страниц памяти процесса были заменены на диск. По крайней мере, они больше не находятся в кешках L1 или L2. В такой ситуации нет смысла использовать приложение для разгрузки для замены всех этих данных и кодовых страниц в память для "освобождения" ресурсов, которые в любом случае будут освобождены операционной системой, когда процесс завершится. Это относится к управляемым и даже к неуправляемым ресурсам. Только ресурсы, которые поддерживают не-фоновые потоки, должны быть удалены, иначе процесс останется в живых.
Теперь во время обычного выполнения есть эфемерные ресурсы, которые необходимо очистить правильно (поскольку @fezmonkey указывает соединения с базой данных, сокеты, дескрипторы окон), чтобы избежать неуправляемых утечек памяти. Это те вещи, которые нужно уничтожить. Если вы создаете какой-то класс, которому принадлежит поток (и по собственному я имею в виду, что он его создал, и поэтому он отвечает за то, чтобы он остановился, по крайней мере, по моему стилю кодирования), тогда этот класс, скорее всего, должен реализовать IDisposable
и снести нить во время Dispose
.
.NET framework использует интерфейс IDisposable
как сигнал, даже предупреждающий разработчиков, что этот класс должен быть удален. Я не могу думать о каких-либо типах в рамках, реализующих IDisposable
(исключая явные реализации интерфейса), где удаление необязательно.
Да, этот код является полностью избыточным и ненужным, и он не делает сборщик мусора делать что-либо, что он не сделал бы иначе (если экземпляр MyCollection выходит за пределы области видимости.) Особенно вызовы .Clear()
.
Отвечайте на свое редактирование: Сортировка. Если я это сделаю:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Он функционально идентичен этому для управления памятью:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Если вам действительно действительно нужно освободить память в этот самый момент, вызовите GC.Collect()
. Однако здесь нет оснований делать это. При необходимости память будет освобождена.
Если вы хотите удалить прямо сейчас, используйте неуправляемую память.
См:
Я не буду повторять обычные вещи о том, как использовать или освобождать неуправляемые ресурсы, которые все были покрыты. Но я хотел бы указать, что кажется распространенным заблуждением.
Учитывая следующий код
Public Class LargeStuff Implements IDisposable Private _Large as string() 'Some strange code that means _Large now contains several million long strings. Public Sub Dispose() Implements IDisposable.Dispose _Large=Nothing End Sub
Я понимаю, что одноразовая реализация не соответствует текущим рекомендациям, но, надеюсь, вы все поняли.
Теперь, когда вызывается Dispose, сколько памяти освобождается?
Ответ: Нет.
Вызов Dispose может освобождать неуправляемые ресурсы, он НЕ МОЖЕТ восстановить управляемую память, только GC может это сделать. Это не означает, что вышеизложенное не является хорошей идеей, следуя приведенной выше схеме, по-прежнему остается хорошей идеей. После того, как Dispose был запущен, ничего не мешает GC повторно запрашивать память, которая используется _Large, хотя экземпляр LargeStuff все еще может быть в области видимости. Строки в _Large также могут быть в гене 0, но экземпляр LargeStuff может быть gen 2, поэтому снова память будет повторно заявлена раньше.
Нет смысла добавлять финализатора для вызова метода Dispose, показанного выше. Это будет просто ЗАДЕРЖАТЬ повторное требование к памяти, чтобы позволить финализатору работать.
LargeStuff
существует достаточно долго, чтобы перейти в Поколение 2, и если _Large
содержит ссылку на вновь созданную строку, которая находится в Поколении 0, то если экземпляр LargeStuff
без обнуления _Large
, то Строка, на которую _Large
будет храниться до следующей коллекции Gen2. Обнуление _Large
может позволить исключить строку в следующей коллекции Gen0. В большинстве случаев обнуление ссылок бесполезно, но в некоторых случаях это может принести некоторую пользу.
В приведенном выше примере он по-прежнему не "освобождает память сейчас". Вся память собрана мусором, но она может позволить сбор памяти в более раннем
Помимо первичного использования в качестве способа управления ресурсом системных ресурсов (полностью охваченных удивительным ответом Ian, kudos!), IDisposable/using может также использоваться для области изменения состояния (критических) глобальных ресурсов: консоли, потоков, процесса, любого глобального объекта, такого как экземпляр приложения. p >
Я написал статью об этом шаблоне: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Он иллюстрирует, как вы можете защитить некоторое часто используемое глобальное состояние в многоразовом и читаемом способе: цвета консоли, текущая культура потоков, свойства объекта приложения Excel...
Во всяком случае, я ожидаю, что код будет менее эффективным, чем при его отсутствии.
Вызов методов Clear() не нужен, и GC, вероятно, не сделал бы этого, если Dispose не сделал этого...
Есть вещи, которые операция Dispose()
делает в примере кода, который может иметь эффект, который не будет происходить из-за обычного GC объекта MyCollection
.
Если объекты, на которые ссылаются _theList
или _theDict
, ссылаются на другие объекты, тогда объект List<>
или Dictionary<>
не будет подлежать сбору, но не будет иметь никакого содержимого. Если бы не было операции Dispose(), как в примере, эти коллекции все равно будут содержать их содержимое.
Конечно, если бы это была ситуация, я бы назвал ее сломанным дизайном - я просто указываю (по-видимому, педантично), что операция Dispose()
может быть не полностью избыточной, в зависимости от того, существуют ли другие виды использования из List<>
или Dictionary<>
, которые не показаны в фрагменте.
Наиболее оправданным вариантом использования для распоряжения управляемыми ресурсами является подготовка к тому, чтобы GC восстановил ресурсы, которые в противном случае никогда не были бы собраны.
Первым примером является круговая ссылка.
В то время как лучше всего использовать шаблоны, которые избегают циклических ссылок, если вы в конечном итоге получаете (например) "дочерний" объект, у которого есть ссылка на его "родительский", это может остановить сбор GC GC родителя, если вы просто отказываетесь от ссылки и полагаетесь на GC-plus, если вы внедрили финализатор, он никогда не будет вызван.
Единственный способ обойти это вручную, чтобы разбить круговые ссылки, установив ссылки родителя на нуль для детей.
Реализация IDisposable для родителей и детей - лучший способ сделать это. Когда Dispose вызывается в Parent, вызовите Dispose для всех дочерних элементов и в методе Dispose для детей установите для родительских ссылок значение null.
WeakReference
, система проверит флаг, который указывает, что в последнем GC была найдена живая WeakReference
ссылка цикл, и либо добавит объект в очередь объектов, нуждающихся в немедленной доработке, освободит объект из кучи больших объектов или аннулирует слабую ссылку. Циркулярные ссылки не сохранят объекты живыми, если другие ссылки не существуют.
Первое из определения. Для меня неуправляемый ресурс означает некоторый класс, который реализует интерфейс IDisposable или что-то созданное с использованием вызовов в dll. GC не знает, как бороться с такими объектами. Если класс имеет, например, только типы значений, то я не рассматриваю этот класс как класс с неуправляемыми ресурсами. Для моего кода я следую следующим практикам:
public class SomeClass : IDisposable
{
/// <summary>
/// As usually I don't care was object disposed or not
/// </summary>
public void SomeMethod()
{
if (_disposed)
throw new ObjectDisposedException("SomeClass instance been disposed");
}
public void Dispose()
{
Dispose(true);
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)//we are in the first call
{
}
_disposed = true;
}
}
is IDisposable
сам по себе должен рассматриваться как неуправляемый ресурс? Это не кажется правильным. Кроме того, если тип внедрения является чистым типом значения, вы, вероятно, предполагаете, что его не нужно удалять. Это также кажется неправильным.
Одна из проблем большинства обсуждений "неуправляемых ресурсов" заключается в том, что они действительно не определяют этот термин, но, похоже, подразумевают, что он имеет какое-то отношение к неуправляемому коду. Хотя верно, что многие типы неуправляемых ресурсов взаимодействуют с неуправляемым кодом, мышление о неуправляемых ресурсах в таких терминах не помогает.
Вместо этого следует понимать, что общего у всех управляемых ресурсов: все они влекут за собой объект, запрашивающий у кого-то вне "вещи" что-то делать от его имени, в ущерб каким-то другим "вещам", а другое лицо соглашается сделайте это до дальнейшего уведомления. Если объект должен был быть оставлен и исчезнут без следа, ничто не могло бы сказать, что вне "вещи" ему больше не нужно было изменять свое поведение от имени объекта, который больше не существовал; следовательно, "полезность вещей будет постоянно уменьшаться".
Таким образом, неуправляемый ресурс представляет собой соглашение какой-либо внешней "вещи", чтобы изменить свое поведение от имени объекта, что бесполезно ухудшало бы полезность этой внешней "вещи", если бы объект был оставлен и прекратил свое существование. Управляемый ресурс - это объект, который является бенефициаром такого соглашения, но который подписал для получения уведомления, если он оставлен, и который будет использовать такое уведомление, чтобы привести свои дела в порядок до его уничтожения.
IDisposable
подходит для отмены подписки на события.
Ваш образец кода не является хорошим примером использования IDisposable
. Обычно очистка словаря не должна идти к методу Dispose
. Элементы словаря будут очищены и удалены, когда они выйдут из сферы действия. IDisposable
требуется освободить некоторую память/обработчики, которые не будут освобождены/освобождены даже после того, как они выйдут из области видимости.
В следующем примере показан хороший пример для шаблона IDisposable с некоторым кодом и комментариями.
public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.
private IntPtr handle;
// Other managed resource this class uses.
private Component component = new Component();
// Track whether Dispose has been called.
private bool disposed = false;
// The class constructor.
public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note disposing has been done.
disposed = true;
}
}
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}
IDisposable
ничего не помечает. МетодDispose
делает то, что должен, для очистки ресурсов, используемых экземпляром. Это не имеет ничего общего с GC.