2009-11-20 9 views
5

Siempre he creído que DataTable consumiría más memoria que una lista genérica. Estoy probando cargar una DataTable y cargar una lista desde una consulta de SQL Server. En este caso, DataTable consume menos memoria. Obtengo las 2000 mejores filas y hay 134 campos por fila. Un campo binario y el resto son varchar estándar, int, bit y así sucesivamente.¿Lista <object[]> usando más memoria que DataTable?

¿Cómo podría una DataTable con todos sus gastos generales consumir menos memoria que List? El GC informa aproximadamente 4mb con DataTable y 5mb con list.

Hice la prueba con algunas de las tablas NorthWind y la lista fue marginalmente menor en esos casos.

private void GetMemory() 
    { 
     label1.Text = string.Format("{0:0.00} MB", GC.GetTotalMemory(true)/1024.0/1024.0);    
    } 
    private void DataTableButton_Click(object sender, EventArgs e) 
    { 
     var conn = new SqlConnection(@"Initial Catalog=ADatabase;Data Source=AServer;Integrated Security=True"); 
     conn.Open(); 

     var cmd = new SqlCommand("SELECT TOP 2000 * FROM AManyColumnedTable", conn); 

     var r = cmd.ExecuteReader(); 
     _rows = new List<object[]>(); 

     //uses more memory 
     object[] a = null; 
     while (r.Read()) 
     { 
      a = new object[r.FieldCount]; 
      r.GetValues(a); 
      _rows.Add(a); 
     } 
     //uses less memory 
     //_table = new DataTable("TheTable"); 
     //_table.Load(r); 

     r.Close(); 
     conn.Close(); 
     GetMemory(); 
    } 

Respuesta

4

que es una ilusión. De alguna manera, el GC no le da el valor correcto, tal vez porque hay objetos que aún no se han recopilado. Quizás cargar una tabla de datos causa más objetos intermedios, lo que causa otra recolección de basura.

Su lista de matrices almacena los datos de la misma manera que la tabla de datos, pero sin la sobrecarga de la información adicional que contiene la tabla de datos y cada fila de datos.

Si desea que sea más eficiente desde el punto de vista de la memoria, debe crear una clase para un registro de datos, de esa manera puede almacenar los tipos de valores como valores simples en lugar de encajonados en objetos. Un int por ejemplo usa 20 bytes cuando está encuadrado pero solo 4 como una variable de miembro simple.

+0

Interesante, intenté llamar a GC.Collect directamente después de cargar la Lista, desafortunadamente no cambió el resultado. Usé GC.Collect (GC.MaxGeneration, GCCollectionMode.Forced) ;. Jugaré con los boxings. No pensé en eso, gracias. Intentaba mantenerlo genérico usando GetValues.Estoy viendo la implementación de DataRow y su uso de una clase DataStorage para contener valores, no un objeto [] como supuse que lo hizo. Nunca he oído hablar de la clase DataStorage, tal vez sería útil. ¡Gracias por la información! +1 – Steve

+0

Tienes razón, fue el boxeo el que consumió la memoria extra. Acabo de intentar cargar los valores de la fila en listas genéricas y las listas hechas usan tipos de datos primitivos no nulables. La memoria era de 3.8mb. El DataTable fue de 4.4mb. ¡Buena llamada! – Steve

1

Sólo por curiosidad, intente llamar TrimExcess en la lista y luego comprobar la memoria. Dudo que esto suponga una gran diferencia, pero sospecho que marcará la diferencia.

Básicamente, una lista crece en lotes. No crece en uno cada vez que agrega un objeto, sino que crece en cierta cantidad, y luego si necesita más, asigna más. Tengo curiosidad por saber si hay un espacio extra en tu lista que no estás usando.

+0

que recibieron el mismo resultado, por desgracia. No pensé en eso, buena publicación. – Steve

+1

Tenga en cuenta que TrimExcess solo hace algo si la cantidad de elementos en la lista es inferior a 0.9 veces la longitud de la matriz. Esta podría ser la diferencia entre 5 MB y 4.5 MB ... explicar un total de 1 MB de diferencia de esta manera es un tramo sin embargo. Como – Joren

+1

la lista en sí no es más grande que unos pocos kilobytes, no es de extrañar que no hay ninguna diferencia notable en el recorte de la misma. La lista solo contiene referencias, los elementos no utilizados son solo referencias nulas, no referencias a matrices vacías. – Guffa

0

Para mí sucedió al revés. Bueno, no estaba obteniendo datos de la base de datos, pero empujando los datos a la base de datos utilizando bulk-insert. Mientras procesaba datos obtuve alrededor de 50 millones de registros como salida que quería almacenar en la base de datos. Solía ​​inserción masiva con 200k filas por empuje utilizando DataTable (que tiene sólo 8 columnas) y se encontró que para cada inserto tardó alrededor de un promedio de 95 MB de datos (herramientas de monitorización de red utilizados y serializador para encontrar el tamaño del objeto). Cuando solía lista (Usando IDataReader) para inserción masiva, se redujo el tamaño de un (un toque) empuje a 15 MB de media (serialización utilizado para encontrar el tamaño del objeto y monitoreado en administrador de tareas). El tiempo necesario para procesar y cargar datos con DataTable fue de 1657 segundos y se redujo a 850 segundos con el enfoque de lista.

Cuestiones relacionadas