Чи слід розпоряджатися () DataSet і DataTable?


197

DataSet і DataTable реалізують IDisposable, тому, за звичайними найкращими методами, я повинен називати їхні методи Dispose ().

Однак, з того, що я читав до цих пір, DataSet і DataTable насправді не мають ніяких керованих ресурсів, тому Dispose () насправді не робить багато.

Крім того, я не можу просто використовувати, using(DataSet myDataSet...)оскільки DataSet має колекцію таблиць даних.

Отже, щоб бути безпечним, мені потрібно переглядати таблиці MyDataSet.Tables, утилізувати кожну з таблиць даних, а потім утилізувати DataSet.

Отже, чи варто зайвих клопотів зателефонувати Dispose () на всі мої набори даних та таблиці даних?

Додаток:

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

Однак це стає некрасивим по-справжньому швидко для колекції. Наприклад, що ви робите, якщо один із викликів Dispose () кинув виняток? Ви проковтнете це (що "погано"), щоб ви могли продовжувати розпоряджатися наступним елементом?

Або ви пропонуєте мені просто зателефонувати на myDataSet.Dispose () і забути про розміщення таблиць даних у myDataSet.Tables?


9
Утилізація не повинна викидати жодних винятків. Якщо це так - написано недостатньо, тож… спробуйте {some.Dispose (); } лову {} має вистачити. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw

3
Я помітив явний витік пам’яті в одному з моїх додатків, який використовує багато об’єктів DataSet. Я не дзвонив .Dispose () або використовував блоки "використання" для цих об'єктів. Отже, я переглянув код і додав блок "використання" до кожного місця, де я створював DataSet або DataTable, і тепер вивільняється пам'ять. Мені здається твердим свідченням того, що .Dispose () насправді необхідний для DataSet і DataTable.
запаморочення.stackoverflow

Відповіді:


147

Ось пара дискусій, що пояснюють, чому Dispose не потрібен для DataSet.

Розряджати чи не розпоряджатися? :

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

Чи слід викликати Dispose на об'єктах DataTable та DataSet? включає деякі пояснення з MVP:

Простір імен system.data (ADONET) не містить некерованих ресурсів. Тому не потрібно утилізувати жодне з тих, доки ви не додали собі щось особливе.

Розуміння методу розпорядження та наборів даних? має коментар від влади Скотта Аллена:

Ми дуже рідко розпоряджаємось набором даних, оскільки він пропонує незначну користь "

Отже, існує єдиний висновок про те, що наразі немає вагомих причин викликати розпорядження на DataSet.


7
Надані посилання повністю пропустили те, що DataTable є типом об'єкта, що закінчується. Відповідь Нарімана дивіться нижче.
Герман

Цікава відповідь, але як щодо SqlConnection, SqlCommand та SqlDataAdapter, чи слід чітко називати Dispose?
Віллі

@ Добро, я думаю, що багато людей використовують оператор використання для IDisposables. використовуючи (SqlConnection cn = новий SqlConnection (connectionString)) {використовуючи (SqlCommand cm = новий SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK

1
@ Вільно так, їх слід абсолютно утилізувати, оскільки вони використовують некеровані ресурси. Незалежно від того, чи буде викликано явно або неявно за допомогою usingблоку, ви вирішите.
D Стенлі

129

Оновлення (1 грудня 2009 р.):

Я хотів би змінити цю відповідь і визнати, що початкова відповідь була помилковою.

Оригінальний аналіз дійсно застосовується до об'єктів, які потребують доопрацювання - і точка, згідно з якою практики не повинні бути прийняті на поверхні без точного, глибокого розуміння, все ще існує.

Однак виявляється, що DataSets, DataViews, DataTables пригнічують доопрацювання у своїх конструкторах - саме тому виклик Dispose () на них явно нічого не робить.

Імовірно, це відбувається тому, що вони не мають некерованих ресурсів; тому, незважаючи на те, що MarshalByValueComponent допускає керування некерованими ресурсами, ці конкретні реалізації не потребують і тому можуть відмовитися від доопрацювання.

(Саме автори .NET піклуються про придушення доопрацювання тих самих типів, які зазвичай займають найбільше пам’яті, говорить про важливість цієї практики в цілому для доопрацьованих типів.)

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

Після багато прочитаного, ось моє розуміння:

Якщо об'єкт потребує доопрацювання, він може займати пам'ять довше, ніж потрібно - ось чому: а) будь-який тип, який визначає деструктор (або успадковує тип, який визначає деструктор), вважається остаточним; б) при виділенні (до запуску конструктора) вказівник розміщується на черзі завершення; c) Об'єкт, що підлягає завершенню, вимагає відновити 2 колекції (замість стандартних 1); d) Придушення завершення не видаляє об'єкт із черги завершення (як повідомляється! FinalizeQueue в SOS) Ця команда вводить в оману; Знання, які об’єкти знаходяться у черзі на завершення (саме по собі), не є корисним; Знання, які об’єкти знаходяться в черзі на завершення та все ще потребують доопрацювання, було б корисним (чи є команда для цього?)

Придушення фіналізації дещо відключається у заголовку об’єкта, вказуючи на час виконання, що йому не потрібно мати виклик його Finalizer (не потрібно переміщувати чергу очередей); Він залишається у черзі на завершення (і продовжує повідомляти! FinalizeQueue в SOS)

Усі класи DataTable, DataSet, DataView вкорінені в MarshalByValueComponent, об'єкт, що може завершитися, який може (потенційно) обробляти некеровані ресурси

  • Оскільки DataTable, DataSet, DataView не вводять керовані ресурси, вони пригнічують доопрацювання у своїх конструкторах
  • Хоча це незвичайний зразок, він звільняє абонента від турботи про виклик утилізувати після використання
  • Це, а також той факт, що DataTables потенційно можна ділитись між різними наборами даних, ймовірно, чому DataSets не піклується про розпорядження дочірніми DataTables
  • Це також означає, що ці об'єкти з'являться під! FinalizeQueue в SOS
  • Однак ці об’єкти все-таки мають бути повернені після єдиної колекції, як і їхні аналоги, що не завершуються

4 (нові посилання):

Оригінальний відповідь:

На це є багато оманливих і взагалі дуже поганих відповідей - кожен, хто приземлився тут, повинен ігнорувати шум і уважно читати посилання нижче.

Без сумніву, Dispose слід викликати будь-які об'єкти, що закінчуються.

Таблиці даних є завершеними.

Calling Dispose значно прискорює повернення пам'яті.

MarshalByValueComponent викликає GC.SuppressFinalize (це) у своєму Dispose () - пропуск цього означає, що доведеться чекати десятки, якщо не сотні колекцій Gen0, перш ніж пам'ять буде відновлена:

З цього базового розуміння доопрацювання ми можемо вже вивести деякі дуже важливі речі:

По-перше, об’єкти, які потребують доопрацювання, живуть довше, ніж об'єкти, які цього не роблять. Насправді вони можуть прожити набагато довше. Наприклад, припустимо, що об’єкт, що знаходиться у gen2, потребує доопрацювання. Фіналізація буде запланована, але об'єкт все ще знаходиться у gen2, тому він не буде повторно зібраний до наступної колекції gen2. Це дійсно може бути дуже довгий час, і, насправді, якщо все піде добре, це буде довгий час, тому що колекції gen2 коштують дорого, тому ми хочемо, щоб вони траплялися дуже рідко. Старіші об'єкти, які потребують доопрацювання, можуть зачекати десятки, якщо не сотні колекцій gen0, перш ніж їх місце буде відтворено.

По-друге, об'єкти, які потребують доопрацювання, завдають пошкодження застави. Оскільки внутрішні покажчики об’єктів повинні залишатися дійсними, не тільки об'єкти, які потребують фіналізації, залишаться в пам'яті, але все, на що об'єкт посилається, прямо та опосередковано, також залишатиметься в пам'яті. Якщо величезне дерево об'єктів було закріплено одним об’єктом, який потребував доопрацювання, то все дерево затрималося б, можливо, протягом тривалого часу, як ми щойно обговорювали. Тому важливо економно використовувати фіналізатори та розміщувати їх на об'єктах, на яких є якомога менше внутрішніх покажчиків об'єктів. У прикладі дерева, який я щойно подав, ви можете легко уникнути проблеми, перемістивши ресурси, що потребують доопрацювання, до окремого об'єкта та зберігаючи посилання на цей об’єкт у корені дерева.

Нарешті, об’єкти, які потребують доопрацювання, створюють роботу для потоку фіналізатора. Якщо ваш процес доопрацювання є складним, один і єдиний потік фіналізатора витратить багато часу на виконання цих кроків, що може спричинити відставання роботи і, отже, призвести до того, що більше об'єктів затримається, чекаючи їх завершення. Тому життєво важливо, щоб фіналізатори виконували якомога менше роботи. Пам'ятайте також, що хоча всі покажчики об'єктів залишаються дійсними під час доопрацювання, можливо, ці вказівники призводять до об'єктів, які вже були остаточно завершені, і тому можуть бути менш корисними. Як правило, найбезпечніше уникати наступних покажчиків об’єктів у фіналізаційному коді, навіть якщо покажчики дійсні. Безпечний, короткий шлях до фіналізації - найкращий.

Візьміть це у того, хто бачив 100 000 МБ неопосиланих 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-performance-using-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 () нічого не робить, тому він пригнічує доопрацювання в конструкторі, тобто тому, що не було би чого робити, він не хоче турбувати GC з викликом фіналізатора ( який зазвичай називає розпорядження).
Марк Гравелл

Дякую. Чи стосується ця дискусія і до TableAdapters?
Бондолін


24

Ви повинні припустити, що він робить щось корисне, і зателефонувати в розпорядження, навіть якщо він нічого не робить в поточному. Втілення NET Framework, немає жодної гарантії, що воно залишиться таким у майбутніх версіях, що призводить до неефективного використання ресурсів.


Немає жодних гарантій, що в майбутньому він також впровадить IDisposable. Я би погодився з вами, якби це було так просто, як використання (...), але у випадку з DataSet це здається великим клопотом ні за що.
mbeckish

28
Досить безпечно припустити, що він завжди буде реалізовувати IDisposable. Додавання або вилучення інтерфейсу є надзвичайно важливою зміною, тоді як зміна реалізації Dispose не відбувається.
Грег Дін

5
Крім того, у іншого постачальника може бути реалізація, яка насправді робить щось із IDisposable.
Метт Спрадлі

Не кажучи вже про те, що DataTableце не запечатано - це не велика справа, коли ви робите new DataTable, але досить важливо, коли ви приймаєте DataTableаргумент або як результат виклику методу.
Луань

17

Навіть якщо об’єкт не має керованих ресурсів, розпорядження може допомогти GC, порушивши графіки об'єктів. Загалом, якщо об'єкт реалізує IDisposable, слід викликати Dispose ().

Будь Dispose () насправді щось робить чи ні, залежить від даного класу. У випадку DataSet реалізація Dispose () успадковується від MarshalByValueComponent. Він видаляє себе з контейнера і викликає подію. Нижній вихідний код (розібраний з .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);
                }
            }
        }
    }
}

1
Справді. Я нещодавно побачив код, де багато DataTables створювались у дуже великому циклі, не будучи розпорядженням. Це призводить до того, що вся пам'ять споживається на комп’ютері, і процес виходить з ладу, коли у нього закінчується пам'ять. Після того, як я сказав розробнику зателефонувати розпоряджатися на DataTable, проблема усунулася.
RichardOD

7

Чи створюєте самі таблиці даних? Тому що ітерація через дітей будь-якого Об'єкта (як у DataSet.Tables) зазвичай не потрібна, оскільки це завдання Батька розпоряджатися всіма членами дитини.

Як правило, правило таке: Якщо ви створили його і він реалізує IDisposable, розпоряджайтесь ним. Якщо ви НЕ створили його, то НЕ розпоряджайтесь ним, це завдання батьківського об'єкта. Але кожен об'єкт може мати спеціальні правила, перевірте Документацію.

Для .net 3.5 він чітко говорить: "Утилізуйте його, коли більше не використовуєте", тому я б це зробив.


4
Як я розумію, загальна думка полягає в тому, що об’єкт повинен розпоряджатися власними некерованими ресурсами. Тим НЕ менше, колекція IDisposable об'єктів не буде взагалі перебирати її елементи утилізувати кожен з них, тому що там може бути і інші посилання на її елементи за межами колекції: stackoverflow.com/questions/496722 / ...
mbeckish

1
Щоправда, колекції - це завжди те, що я вважаю особливим, оскільки вони зазвичай нічого не роблять, вони просто ... Контейнери, тому я ніколи не переймався цим.
Майкл Штум

7

Я закликаю розпоряджатися будь-коли, коли об’єкт реалізує IDisposeable. Там є причина.

Набори даних можуть бути величезними свинями пам'яті. Чим раніше вони можуть бути відмічені для прибирання, тим краще.

оновлення

Минуло 5 років, як я відповів на це запитання. Я все ще погоджуюся зі своєю відповіддю. Якщо є метод розпорядження, його слід викликати, коли ви закінчите з об'єктом. Інтерфейс IDispose був реалізований не просто так.


5
Виклик утилізації не прискорює повернення пам'яті; для цього вам доведеться вручну запустити сміттєзбірник, що, як правило, поганий план.
Tetraneutron

2
Якщо Dispose встановлює купу посилань на null, це може призвести до того, що об'єкти можуть бути кандидатами на збір, які в іншому випадку можуть бути пропущені.
Грег Дін

1
Сенс утилізації - не очищати пам'ять керованих об’єктів - це робота сміттєзбірника. Сенс у тому, щоб очистити некеровані об’єкти. Здається, є свідчення того, що DataSets не мають ніяких керованих посилань, тому теоретично не потрібно їх розпоряджатись. Коли це було сказано, я ніколи не був у ситуації, коли мені довелося вийти зі свого шляху, щоб зателефонувати у розпорядження - я все одно просто зателефонував.
cbp

4
Первинне використання IDisposable є звільнення некерованих ресурсів. Часто він також змінює стан таким чином, що має сенс для розміщеного екземпляра. (тобто властивості встановлені на false, посилання встановлені на null тощо)
Грег Дін,

3
Якщо на об’єкті є метод утилізації, він був поміщений туди з якоїсь причини, незалежно від того, чи це для очищення некерованих об'єктів чи ні.
Чак Конвей

4

Якщо ваш намір або контекст цього питання справді збирання сміття, то ви можете встановити набори даних та таблиці даних, щоб явно обнулити чи використовувати ключове слово, і випустити їх за межі сфери застосування. Утилізація не робить багато, як це сказав раніше Tetraneutron. GC буде збирати об'єкти наборів даних, на які більше не посилаються, а також ті, що не входять до сфери застосування.

Я дуже хочу, щоб ТАК змусив людей відмовитися від голосування, щоб насправді написати коментар, перш ніж відмовлятися від відповіді.


+ 1 Напевно, деякі люди не хочуть дозволяти іншим розглянути різні точки зору.
DOK

2
відмова від голосування, жодним чином не заважає людям розглядати різні точки зору.
Грег Дін

1

Набори даних реалізують IDisposable ретельно MarshalByValueComponent, який реалізує IDisposable. Оскільки наборами даних керують, реальної користі від виклику розпорядження немає.


6
Це може зараз, хто знає, що це зробить пізніше.
Грег Дін

Таке ставлення, в якому ви гадаєте, що будь-який код в майбутньому не буде робити те, що він повинен робити, - це біль у припущенні для всіх учасників.
MicroservicesOnDDD

0

Спробуйте скористатися функцією Clear (). Для мене це чудово справляється з розпорядженням.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();

0

Не потрібно розпоряджатися (), тому що DataSet успадковує клас MarshalByValueComponent та MarshalByValueComponent реалізують інтерфейс IDisposable

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.