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


Answers

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

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

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

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

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

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

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

После многих чтений, вот мое понимание:

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

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

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

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

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

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

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

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

Таблицы данных являются окончательными.

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

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

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

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

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

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

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

Рекомендации:

1 - 1

2 - 2 http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx с http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx

http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

Question

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

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

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

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

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

Приложение:

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

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

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




Прежде всего, я бы проверил, что Dispose делает с DataSet. Возможно, использование рефлектора от redgate поможет.




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




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

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

Обновить

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




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

Действительно ли Dispose () фактически что-то делает или нет, зависит от данного класса. В случае DataSet реализация Dispose () наследуется от MarshalByValueComponent. Он удаляется из контейнера и вызывает Disposed event. Исходный код ниже (разобран с помощью .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);
                }
            }
        }
    }
}