Devo disporre () DataSet e DataTable?


197

DataSet e DataTable implementano entrambi IDisposable, quindi, secondo le migliori pratiche convenzionali, dovrei chiamare i loro metodi Dispose ().

Tuttavia, da quello che ho letto finora, DataSet e DataTable in realtà non hanno risorse non gestite, quindi Dispose () in realtà non fa molto.

Inoltre, non posso semplicemente usarlo using(DataSet myDataSet...)perché DataSet ha una raccolta di DataTables.

Quindi, per sicurezza, avrei bisogno di scorrere attraverso myDataSet.Tables, eliminare ciascuna delle DataTables, quindi eliminare il DataSet.

Quindi vale la pena chiamare Dispose () su tutti i miei DataSet e DataTables?

Addendum:

Per quelli di voi che pensano che DataSet debba essere eliminato: in generale, lo schema per lo smaltimento è di usare usingo try..finally, perché si desidera garantire che verrà chiamato Dispose ().

Tuttavia, questo diventa brutto molto velocemente per una collezione. Ad esempio, cosa fai se una delle chiamate a Dispose () ha generato un'eccezione? Lo ingoiate (che è "cattivo") in modo da poter continuare a smaltire l'elemento successivo?

Oppure, mi suggerisci di chiamare myDataSet.Dispose () e di dimenticare di eliminare le DataTable in myDataSet.Tables?


9
Smaltire non dovrebbe generare eccezioni. In tal caso, non è ben scritto, quindi ... prova {some.Dispose (); } catch {} dovrebbe essere sufficiente. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw

3
Ho notato un'apparente perdita di memoria in una delle mie app che utilizza molti oggetti DataSet. Non avevo chiamato .Dispose () o non ho usato i blocchi "using" per quegli oggetti. Ho quindi esaminato il codice e aggiunto un blocco "using" in ogni posizione in cui stavo creando un DataSet o una DataTable e voilà la memoria è ora rilasciata. Mi sembra una solida indicazione che .Dispose () è, infatti, necessario per DataSet e DataTable.
dizzy.stackoverflow

Risposte:


147

Ecco un paio di discussioni che spiegano perché non è necessario disporre di un DataSet.

Smaltire o non smaltire? :

Il metodo Dispose in DataSet esiste SOLO a causa dell'effetto collaterale dell'ereditarietà, in altre parole, in realtà non fa nulla di utile nella finalizzazione.

Dispose deve essere chiamato su oggetti DataTable e DataSet? include alcune spiegazioni da un MVP:

Lo spazio dei nomi system.data (ADONET) non contiene risorse non gestite. Pertanto non è necessario smaltire nessuno di questi purché non vi sia stato aggiunto qualcosa di speciale.

Comprensione del metodo di eliminazione e dei set di dati? ha un commento dell'autorità Scott Allen:

In pratica raramente disponiamo di un DataSet perché offre pochi vantaggi "

Quindi, il consenso è che attualmente non esiste un buon motivo per chiamare Dispose su un DataSet.


7
I collegamenti forniti hanno completamente perso il punto che DataTable è un tipo di oggetto Finalizable. Di seguito, vedi la risposta di Nariman.
Herman,

Risposta interessante ma che dire di SqlConnection, SqlCommand e SqlDataAdapter, il Dispose dovrebbe essere chiamato esplicitamente?
Willy,

@Willy Penso che molte persone usino un'istruzione using per IDisposables. using (SqlConnection cn = new SqlConnection (connectionString)) {using (SqlCommand cm = new SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK,

1
@Willy sì, quelli dovrebbero essere assolutamente eliminati perché usano risorse non gestite. Dipende esplicitamente o implicitamente dall'uso di un usingblocco.
D Stanley,

129

Aggiornamento (1 dicembre 2009):

Vorrei modificare questa risposta e ammettere che la risposta originale era difettosa.

L'analisi originale si applica agli oggetti che richiedono la finalizzazione e il punto che le pratiche non dovrebbero essere accettate in superficie senza una comprensione accurata e approfondita rimane valido.

Tuttavia, si scopre che DataSet, DataViews, DataTables sopprimono la finalizzazione nei loro costruttori - questo è il motivo per cui chiamare Dispose () su di essi esplicitamente non fa nulla.

Presumibilmente, ciò accade perché non dispongono di risorse non gestite; quindi nonostante il fatto che MarshalByValueComponent tenga conto delle risorse non gestite, queste implementazioni particolari non hanno la necessità e possono quindi rinunciare alla finalizzazione.

(Che gli autori di .NET si preoccuperebbero di sopprimere la finalizzazione sui tipi che normalmente occupano la maggior parte della memoria parla dell'importanza di questa pratica in generale per i tipi finalizzabili.)

Ciononostante, questi dettagli sono ancora sotto documentati poiché la nascita di .NET Framework (quasi 8 anni fa) è piuttosto sorprendente (che in sostanza sei lasciato ai tuoi dispositivi per setacciare materiale ambiguo ma contrastante per mettere insieme i pezzi a volte è frustrante ma fornisce una comprensione più completa del quadro su cui facciamo affidamento ogni giorno).

Dopo molte letture, ecco la mia comprensione:

Se un oggetto richiede la finalizzazione, potrebbe occupare memoria più a lungo del necessario - ecco perché: a) Qualsiasi tipo che definisce un distruttore (o eredita da un tipo che definisce un distruttore) è considerato finalizzabile; b) Al momento dell'allocazione (prima che venga eseguito il costruttore), un puntatore viene posizionato sulla coda di finalizzazione; c) Un oggetto finalizzabile richiede normalmente la raccolta di 2 raccolte (anziché lo standard 1); d) La soppressione della finalizzazione non rimuove un oggetto dalla coda di finalizzazione (come riportato da! FinalizeQueue in SOS) Questo comando è fuorviante; Sapere quali oggetti si trovano nella coda di finalizzazione (di per sé) non è utile; Sapere quali oggetti si trovano nella coda di finalizzazione e richiedono ancora la finalizzazione sarebbe utile (esiste un comando per questo?)

La soppressione della finalizzazione si spegne un po 'nell'intestazione dell'oggetto indicando al runtime che non è necessario richiamare il finalizzatore (non è necessario spostare la coda FReachable); Rimane nella coda di finalizzazione (e continua a essere segnalato da! FinalizeQueue in SOS)

Le classi DataTable, DataSet, DataView sono tutte radicate in MarshalByValueComponent, un oggetto finalizzabile che può (potenzialmente) gestire risorse non gestite

  • Poiché DataTable, DataSet, DataView non introducono risorse non gestite, eliminano la finalizzazione nei loro costruttori
  • Sebbene questo sia un modello insolito, libera il chiamante dal doversi preoccupare di chiamare Dispose dopo l'uso
  • Questo e il fatto che DataTables possono potenzialmente essere condivisi tra diversi DataSet, è probabilmente il motivo per cui i DataSet non si preoccupano di smaltire DataTables figlio
  • Questo significa anche che questi oggetti appariranno sotto! FinalizeQueue in SOS
  • Tuttavia, questi oggetti dovrebbero comunque essere recuperabili dopo una singola raccolta, come le loro controparti non finalizzabili

4 (nuovi riferimenti):

Risposta originale:

Ci sono molte risposte fuorvianti e generalmente molto povere su questo - chiunque sia atterrato qui dovrebbe ignorare il rumore e leggere attentamente i riferimenti qui sotto.

Senza dubbio, Dispose dovrebbe essere chiamato su qualsiasi oggetto Finalizable.

Le tabelle dei dati sono finalizzabili.

La chiamata a Dispose accelera notevolmente il recupero della memoria.

MarshalByValueComponent chiama GC.SuppressFinalize (this) nel suo Dispose () - saltare questo significa dover aspettare dozzine se non centinaia di raccolte Gen0 prima che la memoria venga recuperata:

Con questa comprensione di base della finalizzazione possiamo già dedurre alcune cose molto importanti:

Innanzitutto, gli oggetti che necessitano di finalizzazione vivono più a lungo degli oggetti che non lo fanno. In effetti, possono vivere molto più a lungo. Ad esempio, supponiamo che un oggetto in gen2 debba essere finalizzato. La finalizzazione verrà programmata ma l'oggetto è ancora in gen2, quindi non verrà nuovamente raccolto fino alla successiva raccolta gen2. Potrebbe essere davvero molto tempo e, in effetti, se le cose vanno bene, ci vorrà molto tempo, perché le raccolte gen2 sono costose e quindi vogliamo che accadano molto raramente. Gli oggetti più vecchi che necessitano di finalizzazione potrebbero dover attendere dozzine se non centinaia di raccolte gen0 prima che il loro spazio venga recuperato.

In secondo luogo, gli oggetti che necessitano di finalizzazione causano danni collaterali. Poiché i puntatori agli oggetti interni devono rimanere validi, non solo gli oggetti che necessitano direttamente di finalizzazione rimarranno nella memoria, ma tutto ciò a cui si riferisce l'oggetto, direttamente e indirettamente, rimarrà anche nella memoria. Se un enorme albero di oggetti fosse ancorato da un singolo oggetto che richiedeva la finalizzazione, allora l'intero albero rimarrebbe, potenzialmente per molto tempo, come abbiamo appena discusso. È quindi importante utilizzare i finalizzatori con parsimonia e posizionarli su oggetti con il minor numero possibile di puntatori interni. Nell'esempio dell'albero che ho appena dato, puoi facilmente evitare il problema spostando le risorse che necessitano di finalizzazione su un oggetto separato e mantenendo un riferimento a quell'oggetto nella radice dell'albero.

Infine, gli oggetti che necessitano di finalizzazione creano lavoro per il thread del finalizzatore. Se il processo di finalizzazione è complesso, l'unico thread del finalizzatore impiegherà molto tempo a eseguire tali passaggi, il che può causare un arretrato di lavoro e quindi far indugiare più oggetti in attesa della finalizzazione. Pertanto, è di vitale importanza che i finalizzatori facciano il minor lavoro possibile. Ricordare inoltre che sebbene tutti i puntatori a oggetti rimangano validi durante la finalizzazione, è possibile che tali puntatori portino a oggetti che sono già stati finalizzati e potrebbero quindi essere poco utili. È generalmente più sicuro evitare i seguenti puntatori oggetto nel codice di finalizzazione anche se i puntatori sono validi. Un percorso di codice di finalizzazione sicuro e breve è il migliore.

Prendilo da qualcuno che ha visto centinaia di MB di DataTable senza riferimenti in Gen2: questo è estremamente importante e completamente mancato dalle risposte su questo thread.

Riferimenti:

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-prestazioni-con-finalizedispose-pattern.aspx

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


Buon punto. Come strutturi normalmente il tuo codice quando hai un DataSet con molti DataTables? Tonnellate di istruzioni nidificate usando? Un solo tentativo ... alla fine di ripulire tutto in una volta?
mbeckish,

14
L'affermazione "Tuttavia, si scopre che DataSet, DataViews, DataTables sopprimono la finalizzazione nei loro costruttori - ecco perché chiamare Dipose () su di essi non fa esplicitamente nulla." è un non-sequitur: i due concetti sono in gran parte indipendenti; qualcosa che sopprime la finalizzazione potrebbe ancora fare qualcosa in Dispose (). In effetti, ha più senso se lo invertiamo: Dispose () non fa nulla, motivo per cui sopprime la finalizzazione nel costruttore, ovvero perché non ci sarebbe nulla da fare, non vuole disturbare il GC con la chiamata del finalizzatore ( che in genere chiama dispose).
Marc Gravell

Grazie. Questa discussione vale anche per TableAdapters?
Bondolin


24

Dovresti supporre che faccia qualcosa di utile e chiamare Dispose anche se non fa nulla in corrente. Incarnazioni di NET Framework, non vi è alcuna garanzia che rimarrà tale nelle versioni future portando a un uso inefficiente delle risorse.


Non è garantito che implementerà IDisposable in futuro. Concordo con te se fosse semplice come usare (...), ma nel caso di DataSet, sembra una seccatura per niente.
mbeckish,

28
È abbastanza sicuro supporre che implementerà sempre IDisposable. L'aggiunta o la rimozione dell'interfaccia è un cambiamento decisivo, mentre non lo è cambiare l'implementazione di Dispose.
Greg Dean,

5
Inoltre, un altro fornitore potrebbe avere un'implementazione che effettivamente fa qualcosa con IDisposable.
Matt Spradley,

Per non parlare del fatto che DataTablenon è sigillato - non è un grosso problema quando lo fai new DataTable, ma è molto importante quando prendi DataTableun argomento o come risultato di una chiamata di metodo.
Luaan,

17

Anche se l'oggetto non ha risorse non gestite, lo smaltimento potrebbe aiutare GC rompendo i grafici degli oggetti. In generale, se l'oggetto implementa IDisposable, dovrebbe essere chiamato Dispose ().

Se Dispose () fa effettivamente qualcosa o no dipende da una determinata classe. Nel caso di DataSet, l'implementazione di Dispose () viene ereditata da MarshalByValueComponent. Si rimuove dal contenitore e chiama l'evento Disposed. Il codice sorgente è di seguito (disassemblato con .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
Infatti. Ho visto un po 'di codice abbastanza recentemente in cui molti DataTable sono stati creati in un ciclo molto grande senza essere eliminati. Ciò ha comportato l'esaurimento di tutta la memoria del computer e l'arresto anomalo del processo durante l'esaurimento della memoria. Dopo che ho detto allo sviluppatore di chiamare disposose sulla DataTable il problema è scomparso.
RichardOD,

7

Crea tu stesso le DataTable? Perché l'iterazione attraverso i figli di qualsiasi oggetto (come in DataSet.Tables) di solito non è necessaria, poiché è compito del genitore disporre di tutti i suoi membri figlio.

Generalmente, la regola è: se l'hai creata e implementa IDisposable, eliminala. Se NON l'hai creato, NON lo getti, questo è il lavoro dell'oggetto genitore. Ma ogni oggetto può avere regole speciali, controlla la documentazione.

Per .net 3.5, dice esplicitamente "Smaltiscilo quando non lo usi più", quindi è quello che vorrei fare.


4
Da quanto ho capito, il consenso generale è che un oggetto dovrebbe disporre delle proprie risorse non gestite. Tuttavia, una collezione di oggetti IDisposable non in iterate generale attraverso i suoi elementi di disporre ognuno, perché ci potrebbero essere altri riferimenti ai suoi elementi al di fuori della collezione: stackoverflow.com/questions/496722/...
mbeckish

1
È vero, le Collezioni sono sempre qualcosa che considero speciale perché di solito non "fanno" nulla, sono semplicemente ... Contenitori, quindi non mi sono mai preoccupato di questo.
Michael Stum

7

Chiamo dispose ogni volta che un oggetto implementa IDisposeable. È lì per un motivo.

I set di dati possono essere enormi porci di memoria. Prima possono essere contrassegnati per la pulizia, meglio è.

aggiornare

Sono passati 5 anni da quando ho risposto a questa domanda. Sono ancora d'accordo con la mia risposta. Se esiste un metodo dispose, dovrebbe essere chiamato quando hai finito con l'oggetto. L'interfaccia IDispose è stata implementata per un motivo.


5
La chiamata a dispose non accelera il recupero della memoria, per farlo dovresti avviare manualmente il garbage collector, che generalmente è un cattivo piano.
Tetraneutron,

2
Se Dispose imposta un insieme di riferimenti su null, è possibile che gli oggetti siano candidati alla raccolta che altrimenti potrebbero essere ignorati.
Greg Dean,

1
Lo scopo di Dispose non è quello di liberare la memoria degli oggetti gestiti - questo è il lavoro del Garbage Collector. Il punto è chiarire gli oggetti non gestiti. Sembra che ci siano prove del fatto che i DataSet non abbiano riferimenti non gestiti, quindi teoricamente non è necessario eliminarli. Detto questo, non sono mai stato in una situazione in cui ho dovuto fare di tutto per chiamare Dispose - lo chiamerei comunque.
cbp,

4
L' uso principale di IDisposable è di rilasciare risorse non gestite. Spesso modifica anche lo stato in modo sensato per un'istanza disposta. (ovvero proprietà impostate su false, riferimenti impostati su null, ecc.)
Greg Dean,

3
Se esiste un metodo dispose su un oggetto, è stato inserito lì per un motivo, indipendentemente dal fatto che si tratti di ripulire oggetti non gestiti o meno.
Chuck Conway,

4

Se la tua intenzione o il contesto di questa domanda è davvero la garbage collection, puoi impostare i set di dati e i database in modo esplicito su null o utilizzare la parola chiave usando e lasciarli fuori campo. Smaltire non fa molto come diceva Tetraneutron in precedenza. GC raccoglierà oggetti del set di dati che non fanno più riferimento e anche quelli che non rientrano nell'ambito.

Vorrei davvero che SO costringesse le persone a votare per scrivere un commento prima di votare la risposta.


+ 1 Suppongo che alcune persone non vogliono permettere ad altri di considerare diversi punti di vista.
DOK,

2
il voto contrario, non impedisce in alcun modo alle persone di considerare diversi punti di vista.
Greg Dean,

1

I set di dati implementano MarshalByValueComponent completo IDisposable, che implementa IDisposable. Poiché i set di dati sono gestiti, non vi è alcun vantaggio reale nel chiamare disposose.


6
Può ora, chissà cosa farà dopo.
Greg Dean,

Questo atteggiamento in cui si ipotizza che in futuro qualsiasi codice non farà ciò che dovrebbe fare è una sofferenza nell'ipotesi per tutti i soggetti coinvolti.
MicroserviziDDDD

0

Prova a usare la funzione Clear (). Funziona benissimo per me per lo smaltimento.

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

0

Non è necessario disporre () poiché DataSet eredita la classe MarshalByValueComponent e l'interfaccia IDisposable dell'attrezzo MarshalByValueComponent

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.