¿Debo desechar () DataSet y DataTable?

197

DataSet y DataTable implementan IDisposable, por lo que, según las mejores prácticas convencionales, debería llamar a sus métodos Dispose ().

Sin embargo, por lo que he leído hasta ahora, DataSet y DataTable en realidad no tienen recursos no administrados, por lo que Dispose () en realidad no hace mucho.

Además, no puedo usar using(DataSet myDataSet...)porque DataSet tiene una colección de DataTables.

Entonces, para estar seguro, necesitaría iterar a través de myDataSet.Tables, desechar cada una de las DataTables y luego desechar el DataSet.

Entonces, ¿vale la pena llamar a Dispose () en todos mis DataSets y DataTables?

Apéndice:

Para aquellos de ustedes que piensan que DataSet debe eliminarse: en general, el patrón para eliminar es usar usingo try..finally, porque desean garantizar que se llamará a Dispose ().

Sin embargo, esto se pone feo muy rápido para una colección. Por ejemplo, ¿qué haces si una de las llamadas a Dispose () arrojó una excepción? ¿Se lo traga (que es "malo") para que pueda continuar para deshacerse del siguiente elemento?

¿O sugiere que simplemente llame a myDataSet.Dispose () y me olvide de deshacerme de las DataTables en myDataSet.Tables?

mbeckish
fuente
9
Eliminar no se supone que arroje ninguna excepción. Si lo hace, no está bien escrito, así que ... intente {some.Dispose (); } catch {} debería ser suficiente. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw
3
Noté una aparente pérdida de memoria en una de mis aplicaciones que usa muchos objetos DataSet. No había estado llamando a .Dispose () o usando bloques de "uso" para esos objetos. Entonces, revisé el código y agregué un bloque "usar" a cada lugar donde estaba creando un DataSet o un DataTable, y listo, la memoria ahora está liberada. Me parece una indicación sólida de que .Dispose () es, de hecho, necesario para DataSet y DataTable.
dizzy.stackoverflow

Respuestas:

147

Aquí hay un par de discusiones que explican por qué Dispose no es necesario para un DataSet.

¿Eliminar o no eliminar? :

El método Dispose en DataSet existe SOLO debido al efecto secundario de la herencia; en otras palabras, en realidad no hace nada útil en la finalización.

¿Debería llamarse a Dispose en los objetos DataTable y DataSet? incluye alguna explicación de un MVP:

El espacio de nombres system.data (ADONET) no contiene recursos no administrados. Por lo tanto, no es necesario deshacerse de ninguno de ellos siempre y cuando no se haya agregado algo especial.

¿Comprender el método de eliminación y los conjuntos de datos? tiene un comentario de la autoridad Scott Allen:

En la práctica, rara vez desechamos un conjunto de datos porque ofrece pocos beneficios "

Entonces, el consenso es que actualmente no hay una buena razón para llamar a Dispose en un DataSet.

DOK
fuente
77
Los enlaces proporcionados perdieron totalmente el punto de que DataTable es un tipo de objeto Finalizable. Por favor, vea la respuesta de Nariman a continuación.
Herman el
Respuesta interesante, pero ¿qué pasa con SqlConnection, SqlCommand y SqlDataAdapter? ¿Debería llamarse Dispose explícitamente?
Willy
@Willy Creo que mucha gente usa una declaración de uso para IDisposables. using (SqlConnection cn = new SqlConnection (connectionString)) {using (SqlCommand cm = new SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK
1
@Willy sí, esos deberían eliminarse por completo porque usan recursos no administrados. Si se llama explícita o implícitamente usando un usingbloque, depende de usted.
D Stanley
129

Actualización (1 de diciembre de 2009):

Me gustaría enmendar esta respuesta y reconocer que la respuesta original era defectuosa.

El análisis original no se aplica a los objetos que requieren finalización - y el punto de que las prácticas no deben ser aceptados en la superficie sin una precisa, la comprensión en profundidad sigue en pie.

Sin embargo, resulta que DataSets, DataViews, DataTables suprimen la finalización en sus constructores ; es por eso que llamar a Dispose () en ellos explícitamente no hace nada.

Presumiblemente, esto sucede porque no tienen recursos no administrados; así que a pesar de que MarshalByValueComponent tiene en cuenta los recursos no administrados, estas implementaciones particulares no tienen la necesidad y, por lo tanto, pueden renunciar a la finalización.

(Que los autores de .NET se encargarían de suprimir la finalización en los tipos que normalmente ocupan más memoria habla de la importancia de esta práctica en general para los tipos finalizables).

No obstante, que estos detalles aún están poco documentados desde el inicio de .NET Framework (hace casi 8 años) es bastante sorprendente (que esencialmente se le deja a sus propios dispositivos para tamizar material aunque conflictivo y ambiguo para juntar las piezas). a veces es frustrante, pero proporciona una comprensión más completa del marco en el que confiamos todos los días).

Después de mucha lectura, aquí está mi entendimiento:

Si un objeto requiere finalización, podría ocupar la memoria más de lo necesario; he aquí por qué: a) Cualquier tipo que define un destructor (o hereda de un tipo que define un destructor) se considera finalizable; b) En la asignación (antes de que se ejecute el constructor), se coloca un puntero en la cola de Finalización; c) Un objeto finalizable normalmente requiere que se reclamen 2 colecciones (en lugar del estándar 1); d) La supresión de la finalización no elimina un objeto de la cola de finalización (según lo informado por! FinalizeQueue en SOS) Este comando es engañoso; Saber qué objetos están en la cola de finalización (en sí mismo) no es útil; Sería útil saber qué objetos están en la cola de finalización y aún requieren finalización (¿hay un comando para esto?)

Suprimir la finalización se apaga un poco en el encabezado del objeto, lo que indica al tiempo de ejecución que no necesita que se invoque su Finalizador (no es necesario mover la cola FReachable); Permanece en la cola de Finalización (y continúa siendo informado por! FinalizeQueue en SOS)

Las clases DataTable, DataSet, DataView están todas enraizadas en MarshalByValueComponent, un objeto finalizable que puede (potencialmente) manejar recursos no administrados

  • Debido a que DataTable, DataSet, DataView no introducen recursos no administrados, suprimen la finalización en sus constructores
  • Si bien este es un patrón inusual, libera a la persona que llama de tener que preocuparse por llamar a Descartar después de usar
  • Esto, y el hecho de que DataTables potencialmente se pueden compartir entre diferentes DataSets, es probable que los DataSets no deseen deshacerse de DataTables secundarios.
  • Esto también significa que estos objetos aparecerán debajo de! FinalizeQueue en SOS
  • Sin embargo, estos objetos deben ser recuperables después de una sola colección, como sus contrapartes no finalizables.

4 (nuevas referencias):

Respuesta original

Hay muchas respuestas engañosas y generalmente muy pobres sobre esto: cualquiera que haya aterrizado aquí debe ignorar el ruido y leer cuidadosamente las referencias a continuación.

Sin lugar a dudas, Dispose se debe invocar en cualquier objeto Finalizable.

Las tablas de datos son finalizables.

Calling Dispose acelera significativamente la recuperación de memoria.

MarshalByValueComponent llama a GC.SuppressFinalize (this) en su Dispose (); omitir esto significa tener que esperar docenas, si no cientos de colecciones Gen0 antes de recuperar la memoria:

Con esta comprensión básica de la finalización, ya podemos deducir algunas cosas muy importantes:

Primero, los objetos que necesitan finalización viven más que los objetos que no. De hecho, pueden vivir mucho más. Por ejemplo, supongamos que un objeto que está en gen2 necesita ser finalizado. La finalización se programará pero el objeto todavía está en gen2, por lo que no se volverá a recopilar hasta que ocurra la próxima colección gen2. Eso podría ser mucho tiempo y, de hecho, si las cosas van bien, será mucho tiempo, porque las colecciones gen2 son costosas y, por lo tanto, queremos que sucedan con poca frecuencia. Los objetos más antiguos que necesitan finalización podrían tener que esperar docenas, si no cientos de colecciones gen0 antes de recuperar su espacio.

En segundo lugar, los objetos que necesitan finalización causan daños colaterales. Dado que los punteros de objetos internos deben permanecer válidos, no solo los objetos que necesitan finalización permanecen en la memoria sino que todo lo que el objeto hace referencia, directa e indirectamente, también permanecerá en la memoria. Si un gran árbol de objetos estuviera anclado por un solo objeto que requiriera finalización, entonces todo el árbol se demoraría, potencialmente durante mucho tiempo, como acabamos de comentar. Por lo tanto, es importante utilizar los finalizadores con moderación y colocarlos en objetos que tengan la menor cantidad posible de punteros internos. En el ejemplo del árbol que acabo de dar, puede evitar fácilmente el problema moviendo los recursos que necesitan finalización a un objeto separado y manteniendo una referencia a ese objeto en la raíz del árbol.

Finalmente, los objetos que necesitan finalización crean trabajo para el hilo finalizador. Si su proceso de finalización es complejo, el único hilo finalizador pasará mucho tiempo realizando esos pasos, lo que puede causar una acumulación de trabajo y, por lo tanto, provocar que más objetos permanezcan esperando la finalización. Por lo tanto, es de vital importancia que los finalizadores hagan el menor trabajo posible. Recuerde también que, aunque todos los punteros de objetos permanecen válidos durante la finalización, puede ser el caso de que esos punteros conduzcan a objetos que ya se hayan finalizado y, por lo tanto, no sean útiles. Por lo general, es más seguro evitar seguir punteros a objetos en el código de finalización aunque los punteros sean válidos. Una ruta de código de finalización corta y segura es la mejor.

Tómelo de alguien que ha visto cientos de MB de tablas de datos no referenciadas en Gen2: esto es muy importante y las respuestas en este hilo lo han perdido por completo.

Referencias

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/

Nariman
fuente
Buen punto. ¿Cómo estructura generalmente su código cuando tiene un DataSet con muchas DataTables? ¿Toneladas de instrucciones anidadas? Un solo intento ... ¿finalmente limpiar todo de una vez?
mbeckish
14
La declaración "Sin embargo, resulta que los DataSets, DataViews, DataTables suprimen la finalización en sus constructores, es por eso que llamar a Dipose () explícitamente no hace nada". no es sequitur: los dos conceptos no están relacionados en gran medida; algo que suprime la finalización aún podría hacer algo en Dispose (). De hecho, tiene más sentido si lo revertimos: Dispose () no hace nada, por lo que suprime la finalización en el constructor, es decir, porque no habría nada que hacer, no quiere molestar al GC con llamar al finalizador ( que normalmente llama disponer).
Marc Gravell
Gracias. ¿Esta discusión se aplica también a TableAdapters?
Bondolin
24

Debe asumir que hace algo útil y llamar a Dispose incluso si no hace nada en corriente. Encarnaciones de NET Framework, no hay garantía de que se mantenga así en futuras versiones que conduzcan a un uso ineficiente de los recursos.

Nuno
fuente
Tampoco hay garantía de que implementará IDisposable en el futuro. Estoy de acuerdo con usted si fuera tan simple como usar (...), pero en el caso de DataSet, parece una gran molestia para nada.
mbeckish
28
Es bastante seguro asumir que siempre implementará IDisposable. Agregar o quitar la interfaz es un cambio radical, mientras que cambiar la implementación de Dispose no lo es.
Greg Dean
55
Además, un proveedor diferente puede tener una implementación que realmente hace algo con IDisposable.
Matt Spradley
Sin mencionar que DataTableno está sellado: no es un gran problema cuando lo estás haciendo new DataTable, pero es muy importante cuando tomas DataTableun argumento o como resultado de una llamada a un método.
Luaan
17

Incluso si el objeto no tiene recursos no administrados, la eliminación podría ayudar a GC al romper los gráficos de los objetos. En general, si el objeto implementa IDisposable, se debe llamar a Dispose ().

Si Dispose () realmente hace algo o no depende de la clase dada. En el caso de DataSet, la implementación Dispose () se hereda de MarshalByValueComponent. Se elimina del contenedor y llama al evento Disposed. El código fuente está debajo (desmontado 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);
                }
            }
        }
    }
}
dwieczor
fuente
1
En efecto. Hace poco vi un código en el que se crearon muchas tablas de datos en un bucle muy grande sin ser eliminadas. Esto condujo a que toda la memoria se consumiera en la computadora y el proceso se bloqueara al quedarse sin memoria. Después de decirle al desarrollador que llamara a disponer en DataTable, el problema desapareció.
RichardOD
7

¿Crea usted las tablas de datos usted mismo? Debido a que iterar a través de los elementos secundarios de cualquier objeto (como en DataSet.Tables) generalmente no es necesario, ya que es el trabajo del padre eliminar todos sus miembros secundarios.

En general, la regla es: si lo creó e implementa IDisposable, deséchelo. Si NO lo creó, NO lo deseche, ese es el trabajo del objeto primario. Pero cada objeto puede tener reglas especiales, consulte la Documentación.

Para .net 3.5, dice explícitamente "Deséchelo cuando ya no lo use", así que eso es lo que haría.

Michael Stum
fuente
44
Por lo que entiendo, el consenso general es que un objeto debe disponer de sus propios recursos no administrados. Sin embargo, una colección de objetos IDisposable en general no iterará a través de sus elementos para disponer de cada uno, porque puede haber otras referencias a sus elementos fuera de la colección: stackoverflow.com/questions/496722/…
mbeckish
1
Es cierto que las Colecciones siempre son algo que considero especial porque generalmente no están "haciendo" nada, son simplemente ... Contenedores, así que nunca me molesté en eso.
Michael Stum
7

Llamo a dispose cada vez que un objeto implementa IDisposeable. Está ahí por una razón.

Los conjuntos de datos pueden ser enormes cerdos de memoria. Cuanto antes se puedan marcar para la limpieza, mejor.

actualizar

Han pasado 5 años desde que respondí esta pregunta. Todavía estoy de acuerdo con mi respuesta. Si hay un método de eliminación, debe llamarse cuando haya terminado con el objeto. La interfaz IDispose se implementó por una razón.

Chuck Conway
fuente
55
Llamar a dispose no acelera la recuperación de memoria, para hacer eso tendría que iniciar manualmente el recolector de basura, que generalmente es un mal plan.
Tetraneutron
2
Si Dispose establece un grupo de referencias en nulo, puede hacer que los objetos sean candidatos para la colección que de otro modo podrían omitirse.
Greg Dean
1
El objetivo de Dispose no es limpiar la memoria de los objetos administrados, ese es el trabajo del recolector de basura. El punto es aclarar objetos no administrados. Parece haber evidencia de que los DataSets no tienen referencias no administradas, por lo que teóricamente no es necesario que se hayan eliminado. Dicho esto, nunca he estado en una situación en la que haya tenido que hacer todo lo posible para llamar a Dispose, simplemente lo llamaría de todos modos.
cbp
44
El uso principal de IDisposable es liberar recursos no administrados. Muchas veces también modifica el estado de una manera que tiene sentido para una instancia eliminada. (es decir, propiedades establecidas en falso, referencias establecidas en nulo, etc.)
Greg Dean,
3
Si hay un método de eliminación en un objeto, se colocó allí por una razón, independientemente de si es para limpiar objetos no administrados o no.
Chuck Conway el
4

Si su intención o el contexto de esta pregunta es realmente la recolección de basura, puede configurar los conjuntos de datos y las tablas de datos para que sean nulos explícitamente o usar la palabra clave usando y dejarlos fuera de alcance. Desechar no hace mucho como Tetraneutron lo dijo antes. GC recopilará objetos del conjunto de datos que ya no están referenciados y también aquellos que están fuera del alcance.

Realmente deseo que SO obligue a las personas a votar para escribir un comentario antes de votar negativamente la respuesta.

Srikar Doddi
fuente
+ 1 Supongo que algunas personas no quieren permitir que otros consideren diferentes puntos de vista.
DOK
2
votar hacia abajo, de ninguna manera impide que las personas consideren diferentes puntos de vista.
Greg Dean
1

Los conjuntos de datos implementan IDisposable a través de MarshalByValueComponent, que implementa IDisposable. Dado que los conjuntos de datos se administran, no hay un beneficio real en la disposición de llamadas.

Tetraneutrón
fuente
66
Puede ahora, quién sabe lo que hará más tarde.
Greg Dean
Esta actitud en la que especula que cualquier código no hará en el futuro lo que se supone que debe hacer es una pena para todos los involucrados.
MicroservicesOnDDD
0

Intente usar la función Clear (). Me funciona muy bien para deshacerme.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
Hasan Savran
fuente
0

No es necesario disponer () porque DataSet hereda la clase MarshalByValueComponent y MarshalByValueComponent implementa la interfaz IDisposable

Sunil Dhappadhule
fuente