Должен ли я Dispose () DataSet и DataTable?

197

DataSet и DataTable оба реализуют IDisposable, поэтому, согласно общепринятым рекомендациям, я должен вызывать их методы Dispose ().

Однако из того, что я прочитал, DataSet и DataTable на самом деле не имеют никаких неуправляемых ресурсов, поэтому Dispose () на самом деле ничего не делает.

Кроме того, я не могу просто использовать, using(DataSet myDataSet...)потому что DataSet имеет коллекцию DataTables.

Поэтому, чтобы быть в безопасности, мне нужно было бы перебрать myDataSet.Tables, избавиться от каждого из DataTables, а затем избавиться от DataSet.

Итак, стоит ли вызывать Dispose () для всех моих DataSets и DataTables?

Приложение:

Для тех из вас, кто считает, что DataSet должен быть утилизирован: в общем, шаблон для утилизации должен использоваться usingили try..finally, потому что вы хотите гарантировать, что будет вызван Dispose ().

Тем не менее, это становится ужасно быстро для коллекции. Например, что вы делаете, если один из вызовов Dispose () выдал исключение? Вы проглатываете это (что «плохо»), чтобы вы могли продолжить распоряжаться следующим элементом?

Или вы предлагаете мне просто вызвать myDataSet.Dispose () и забыть об удалении DataTables в myDataSet.Tables?

mbeckish
источник
9
Утилизация не должна бросать какие-либо исключения. Если это так - это не очень хорошо написано, так что ... try {some.Dispose (); } catch {} должно быть достаточно. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw
3
Я заметил явную утечку памяти в одном из моих приложений, которое использует много объектов DataSet. Я не вызывал .Dispose () и не использовал блоки "using" для этих объектов. Итак, я просмотрел код и добавил блок «using» в каждое место, где я создавал DataSet или DataTable, и теперь память освобождается. Мне кажется убедительным признаком того, что .Dispose (), на самом деле, необходим для DataSet и DataTable.
dizzy.stackoverflow

Ответы:

147

Вот несколько обсуждений, объясняющих, почему Dispose не является необходимым для DataSet.

Распорядиться или не распорядиться? :

Метод Dispose в DataSet существует ТОЛЬКО из-за побочного эффекта наследования - другими словами, он фактически не делает ничего полезного в финализации.

Следует ли вызывать Dispose для объектов DataTable и DataSet? включает в себя некоторые объяснения от MVP:

Пространство имен system.data (ADONET) не содержит неуправляемых ресурсов. Поэтому нет необходимости избавляться от любого из них, если вы не добавили к нему что-то особенное.

Понимание метода Dispose и наборов данных? имеет комментарий от авторитета Скотта Аллена:

На практике мы редко располагаем набором данных, потому что он дает мало преимуществ »

Таким образом, существует общее мнение, что в настоящее время нет веских причин для вызова Dispose для DataSet.

ДОК
источник
7
Предоставленные ссылки полностью упускают из виду тот факт, что DataTable является типом объекта Finalizable. Пожалуйста, смотрите ответ Наримана ниже.
Герман
Интересный ответ, но как насчет SqlConnection, SqlCommand и SqlDataAdapter, должен ли Dispose вызываться явно?
Вилли
@ Я думаю, многие люди используют оператор использования для IDisposables. using (SqlConnection cn = new SqlConnection (connectionString)) {using (SqlCommand cm = new SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK
1
@ Да, они должны быть уничтожены, потому что они используют неуправляемые ресурсы. Независимо от того, usingвызывается ли он явно или неявно с помощью блока, решать вам.
D Стэнли
129

Обновление (1 декабря 2009 г.):

Я хотел бы изменить этот ответ и признать, что первоначальный ответ был ошибочным.

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

Однако оказывается, что DataSets, DataViews, DataTables подавляют финализацию в своих конструкторах - вот почему вызов Dispose () для них явно ничего не делает.

Предположительно, это происходит потому, что у них нет неуправляемых ресурсов; поэтому, несмотря на тот факт, что MarshalByValueComponent учитывает неуправляемые ресурсы, эти конкретные реализации не нуждаются и поэтому могут отказаться от завершения.

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

Несмотря на то, что эти детали все еще недостаточно документированы с момента создания .NET Framework (почти 8 лет назад), довольно удивительно (что вы по сути оставлены на своих собственных устройствах, чтобы просеивать хотя и противоречивые, неоднозначные материалы, чтобы собрать воедино кусочки). иногда разочаровывает, но обеспечивает более полное понимание структуры, на которую мы полагаемся каждый день).

После большого чтения, вот мое понимание:

Если объект требует финализации, он может занимать память дольше, чем нужно - вот почему: a) любой тип, который определяет деструктор (или наследует от типа, который определяет деструктор), считается финализуемым; б) При выделении (до запуска конструктора) указатель помещается в очередь финализации; c) Для финализуемого объекта обычно требуется 2 коллекции (вместо стандартной 1); d) Подавление финализации не удаляет объект из очереди финализации (как сообщает! FinalizeQueue в SOS) Эта команда вводит в заблуждение; Знание того, какие объекты находятся в очереди завершения (само по себе), не помогает; Было бы полезно знать, какие объекты находятся в очереди завершения и все еще требуют завершения (есть ли команда для этого?)

Подавление завершения выключает немного в заголовке объекта, указывая среде выполнения, что ему не нужно вызывать свой Finalizer (не нужно перемещать очередь FReachable); Он остается в очереди Финализации (и продолжает сообщаться! FinalizeQueue в SOS)

Классы DataTable, DataSet, DataView имеют корни в MarshalByValueComponent, финализируемом объекте, который может (потенциально) обрабатывать неуправляемые ресурсы.

  • Поскольку DataTable, DataSet, DataView не представляют неуправляемые ресурсы, они подавляют завершение в своих конструкторах
  • Хотя это необычный шаблон, он освобождает вызывающего абонента от необходимости беспокоиться о вызове Dispose после использования.
  • Это и тот факт, что DataTable потенциально могут совместно использоваться различными DataSets, вероятно, поэтому DataSets не заботятся о том, чтобы избавиться от дочерних DataTables.
  • Это также означает, что эти объекты появятся под! FinalizeQueue в SOS
  • Тем не менее, эти объекты должны быть восстановлены после одной коллекции, как и их не финализируемые аналоги

4 (новые ссылки):

Оригинальный ответ:

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

Без сомнения, Dispose должен вызываться для любых объектов Finalizable.

DataTables являются Завершаемыми.

Вызов Dispose значительно ускоряет восстановление памяти.

MarshalByValueComponent вызывает GC.SuppressFinalize (this) в своем Dispose () - пропустить это означает, что нужно ждать десятки, если не сотни коллекций Gen0, прежде чем память будет освобождена:

С этим базовым пониманием завершения мы уже можем вывести некоторые очень важные вещи:

Во-первых, объекты, которые требуют доработки, живут дольше, чем объекты, которые этого не делают. На самом деле они могут жить намного дольше. Например, предположим, что объект в gen2 должен быть завершен. Завершение будет запланировано, но объект все еще находится в gen2, поэтому он не будет повторно собран до следующей коллекции gen2. Это действительно может быть очень долго, и, на самом деле, если дела пойдут хорошо, это будет долгое время, потому что коллекции gen2 дорогостоящие, и поэтому мы хотим, чтобы они происходили очень редко. Старые объекты, нуждающиеся в доработке, могут ждать десятки, если не сотни коллекций gen0, прежде чем освободить их пространство.

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

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

Возьмите его у кого-то, кто видел сотни МБ не ссылочных DataTables в Gen2: это очень важно и совершенно не замечено в ответах в этой теме.

Ссылки:

1 - http://msdn.microsoft.com/en-us/library/ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-производительность с помощью-finalizedispose-pattern.aspx

3 - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

Нариман
источник
Хорошая точка зрения. Как вы обычно структурируете свой код, когда у вас есть DataSet со многими DataTables? Тонны вложенных с помощью операторов? Одна попытка .. окончательно очистить все сразу?
Mbeckish
14
Утверждение «Однако оказывается, что DataSets, DataViews, DataTables подавляют финализацию в своих конструкторах - вот почему вызов Dipose () для них явно ничего не делает». это не секвитур: две концепции в значительной степени не связаны; что-то, что подавляет финализацию, все еще может что-то делать в Dispose (). На самом деле, это имеет больше смысла, если мы изменим его: Dispose () ничего не делает, поэтому он подавляет финализацию в конструкторе, т. Е. Потому что нечего делать, он не хочет беспокоить сборщик мусора вызовом финализатора ( который обычно называет утилизировать).
Марк Гравелл
Спасибо. Это обсуждение относится и к TableAdapterс?
Бондолин
24

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

Нуно
источник
Также нет гарантии, что он будет реализовывать IDisposable в будущем. Я бы согласился с вами, если бы это было так же просто, как использовать (...), но в случае с DataSet, это кажется большим хлопотом впустую.
Mbeckish
28
Можно с уверенностью предположить, что он всегда будет реализовывать IDisposable. Добавление или удаление интерфейса является серьезным изменением, тогда как изменение реализации Dispose - нет.
Грег Дин
5
Кроме того, другой поставщик может иметь реализацию, которая на самом деле что-то делает с IDisposable.
Мэтт Спрэдли
Не говоря уже о том, что DataTableэто не запечатано - не так уж важно, когда вы делаете new DataTable, но довольно важно, когда принимаете DataTableв качестве аргумента или в результате вызова метода.
Luaan
17

Даже если у объекта нет неуправляемых ресурсов, утилизация может помочь GC, разбивая графы объектов. В общем, если объект реализует IDisposable, тогда должен быть вызван Dispose ().

Делает ли Dispose () что-то или нет, зависит от данного класса. В случае DataSet реализация Dispose () наследуется от MarshalByValueComponent. Он удаляет себя из контейнера и вызывает событие Disposed. Исходный код ниже (разобран с помощью .NET Reflector):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
dwieczor
источник
1
На самом деле. Совсем недавно я видел некоторый код, в котором множество DataTables было создано в очень большом цикле без удаления. Это приводит к тому, что вся память используется на компьютере и происходит сбой процесса при исчерпании памяти. После того, как я сказал разработчику вызвать dispose на DataTable, проблема ушла.
RichardOD
7

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

Как правило, правило таково: если вы создали его, и он реализует IDisposable, утилизируйте его. Если вы НЕ создали его, то НЕ утилизируйте его, это работа родительского объекта. Но у каждого объекта могут быть свои особые правила, проверьте документацию.

Для .net 3.5, он явно говорит «Утилизируйте его, когда он больше не используется», так что я бы так и сделал.

Майкл Стум
источник
4
Из того, что я понимаю, общий консенсус заключается в том, что объект должен распоряжаться своими неуправляемыми ресурсами. Однако коллекция объектов IDisposable, как правило, не выполняет итерацию по своим элементам, чтобы расположить каждый из них, поскольку могут существовать другие ссылки на ее элементы за пределами коллекции: stackoverflow.com/questions/496722/…
mbeckish
1
Правда, Коллекции - это всегда то, что я считаю особенным, потому что они обычно ничего не «делают», они просто ... Контейнеры, поэтому я никогда не беспокоился об этом.
Майкл Стум
7

Я вызываю dispose в любое время, когда объект реализует IDisposeable. Это там по причине.

DataSets может быть огромным занятием памяти. Чем раньше они могут быть помечены для очистки, тем лучше.

Обновить

Прошло 5 лет с тех пор, как я ответил на этот вопрос. Я все еще согласен с моим ответом. Если есть метод dispose, его следует вызывать, когда вы закончите с объектом. Интерфейс IDispose был реализован по причине.

Чак Конвей
источник
5
Вызов dispose не ускоряет восстановление памяти, для этого вам придется вручную запускать сборщик мусора, что обычно является плохим планом.
Tetraneutron
2
Если Dispose устанавливает группу ссылок на null, это может привести к тому, что объекты станут кандидатами в коллекцию, которые в противном случае могли бы быть пропущены.
Грег Дин
1
Смысл Dispose не в том, чтобы очистить память от управляемых объектов - это работа сборщика мусора. Дело в том, чтобы очистить неуправляемые объекты. Кажется, есть доказательства того, что у DataSets нет неуправляемых ссылок, поэтому теоретически не нужно избавляться от вызывающих их. При этом я никогда не сталкивался с ситуацией, когда мне пришлось бы изо всех сил позвонить в Dispose - я бы все равно позвонил.
cbp
4
Первичное использование IDisposable является освобождение неуправляемых ресурсов. Часто это также изменяет состояние таким образом, который имеет смысл для удаленного экземпляра. (т.е. свойства имеют значение false, ссылки имеют значение null и т. д.)
Грег Дин
3
Если на объекте есть метод dispose, он был помещен туда по причине, независимо от того, предназначен ли он для очистки неуправляемых объектов или нет.
Чак Конвей
4

Если ваше намерение или контекст этого вопроса на самом деле является сборкой мусора, то вы можете установить для наборов данных и таблиц данных значение null в явном виде или использовать ключевое слово using и позволить им выйти из области видимости. Утилизация не так много, как сказал ранее Tetraneutron. GC будет собирать объекты набора данных, на которые больше нет ссылок, а также те, которые находятся вне области видимости.

Я действительно хочу, чтобы НАСТОЯЩИЕ люди заставляли голосовать, чтобы написать комментарий, прежде чем опровергнуть ответ.

Срикар Додди
источник
+1 Я думаю, что некоторые люди не хотят, чтобы другие рассматривали разные точки зрения.
DOK
2
голосование вниз, ни в коем случае не запрещает людям рассматривать разные точки зрения.
Грег Дин
1

Наборы данных реализуют IDisposable основательно MarshalByValueComponent, который реализует IDisposable. Поскольку наборы данных управляются, нет никакой реальной выгоды от вызова утилизации.

тетранейтрона
источник
6
Может сейчас, кто знает, что будет делать позже.
Грег Дин
Такое отношение, при котором вы размышляете, что любой код в будущем не будет делать то, что должен делать, - это боль в предположении для всех участников.
MicroservicesOnDDD
0

Попробуйте использовать функцию Clear (). Это прекрасно работает для меня для утилизации.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
Хасан Савран
источник
0

Нет необходимости в Dispose (), поскольку DataSet наследует класс MarshalByValueComponent, а MarshalByValueComponent реализует интерфейс IDisposable.

Сунил Дхаппадхуле
источник