2009-08-18 16 views
8

Estoy usando VSTS2008 + C# + .Net 3.5 para ejecutar esta aplicación de consola en x64 Server 2003 Enterprise con memoria física 12G.extraña excepción de falta de memoria durante la serialización

Aquí está mi código, y me parece que al ejecutar la instrucción bformatter.Serialize (stream, table), hay una excepción de falta de memoria. Supervisé el uso de la memoria a través de la pestaña Perormance del Administrador de tareas y me parece que solo se utiliza la memoria física 2G cuando se lanza la excepción, por lo que no debería estar fuera de la memoria. :-(

Cualquier idea lo que está mal Cualquier limitación de .Net serialización

static DataTable MakeParentTable() 
    { 
     // Create a new DataTable. 
     System.Data.DataTable table = new DataTable("ParentTable"); 
     // Declare variables for DataColumn and DataRow objects. 
     DataColumn column; 
     DataRow row; 

     // Create new DataColumn, set DataType, 
     // ColumnName and add to DataTable.  
     column = new DataColumn(); 
     column.DataType = System.Type.GetType("System.Int32"); 
     column.ColumnName = "id"; 
     column.ReadOnly = true; 
     column.Unique = true; 
     // Add the Column to the DataColumnCollection. 
     table.Columns.Add(column); 

     // Create second column. 
     column = new DataColumn(); 
     column.DataType = System.Type.GetType("System.String"); 
     column.ColumnName = "ParentItem"; 
     column.AutoIncrement = false; 
     column.Caption = "ParentItem"; 
     column.ReadOnly = false; 
     column.Unique = false; 
     // Add the column to the table. 
     table.Columns.Add(column); 

     // Make the ID column the primary key column. 
     DataColumn[] PrimaryKeyColumns = new DataColumn[1]; 
     PrimaryKeyColumns[0] = table.Columns["id"]; 
     table.PrimaryKey = PrimaryKeyColumns; 

     // Create three new DataRow objects and add 
     // them to the DataTable 
     for (int i = 0; i <= 5000000; i++) 
     { 
      row = table.NewRow(); 
      row["id"] = i; 
      row["ParentItem"] = "ParentItem " + i; 
      table.Rows.Add(row); 
     } 

     return table; 
    } 

    static void Main(string[] args) 
    { 
     DataTable table = MakeParentTable(); 
     Stream stream = new MemoryStream(); 
     BinaryFormatter bformatter = new BinaryFormatter(); 
     bformatter.Serialize(stream, table); // out of memory exception here 
     Console.WriteLine(table.Rows.Count); 

     return; 
    } 

gracias de antemano, George

+1

(respondió a los comentarios) –

+0

Gracias Marc, su respuesta es tan grande y me han marcado como respuesta. Apreciar si pudieras compartir tu experiencia sobre la memoria virtual aquí, http://stackoverflow.com/questions/1297797/windows-32-bit-virtual-memory-page-mapping-issue – George2

Respuesta

10

Nota:?? DataTable por defecto en el formato XML de serialización que se utilizó en . 1. *, que es increíblemente ineficiente Una cosa a intentar es de conmutación al nuevo formato de:

dt.RemotingFormat = System.Data.SerializationFormat.Binary; 

Re the out-of-memory/2GB; objetos .NET individuales (como el byte[] detrás de un MemoryStream) están limitados a 2 GB. Tal vez intente escribir en un FileStream en su lugar?

(edit: nop: trató de que, aún errores)

también me pregunto si se puede lograr mejores resultados (en este caso) utilizando table.WriteXml(stream), tal vez con la compresión GZIP, como si el espacio es un bien escaso.

+0

Hola Marc, gracias por tu valiosa respuesta . Para la limitación de byte [] de tamaño 2G, ¿podría recomendarme más documentos sobre este tema, por favor? Nunca sé esto y quiero aprender más antecedentes. – George2

+0

"editar: no: intenté eso, todavía hay errores" - ¿qué quiere decir con eso, aún con errores? ¿Quieres decir qué método todavía tiene errores? – George2

+0

Marc, he intentado usar writexml y funciona. Pero el tamaño del archivo resultante es solo de 520M. Es mucho menos que 2G. ¿Cómo compruebas que mi código original coincide con la limitación 2G? – George2

1

1) El sistema operativo es x64, pero ¿es la aplicación x64 (o anycpu)? Si no, tiene un límite de 2 Gb.

2) ¿Esto sucede "desde el principio" o después de que la aplicación ha estado funcionando durante algún tiempo (es decir, n serializaciones posteriores)? ¿Podría ser tal vez el resultado de una gran fragmentación del montón de objetos ...?

+0

Gracias KristoferA, por sus preocupaciones, 1. Incorporo a cualquier CPU, por lo que debería poder consumir más de 2G de memoria física, ¿correcto? 2. La excepción de falta de memoria ocurre después de 1 minuto, después de que el método MakeParentTable devuelve éxito. Entonces, ¿no debería ser el resultado de una gran fragmentación de montón de objetos? ¿Algún comentario? – George2

1

Curiosamente, en realidad llega a los 3.7GB antes de dar un error de memoria aquí (Windows 7 x64). Aparentemente, necesitaría aproximadamente el doble de esa cantidad para completarse.

Teniendo en cuenta que la aplicación utiliza 1.65GB después de crear la tabla, parece probable que está golpeando los 2 GB byte[] (o cualquier objeto individual) límite de Marc Gravell está hablando de (1.65GB + 2 GB ~ = 3.7GB)

Basado en este blog, supongo que podría asignar su memoria usando WINAPI, y escribir su propia implementación de MemoryStream usando eso. Es decir, si realmente quieres hacer esto. O escriba uno usando más de una matriz del curso :)

+0

Hola Thorarin, 1. para la limitación de tamaño 2G de byte [], quiero aprender más. ¿Tienes más documentos relacionados? 2. ¿El número de 1.65GB y 2GB significa su huella en su cálculo? – George2

+1

1.65GB para la DataTable misma. Todo lo que pude encontrar en el límite de 2GB (aparte de que existe) es http://blogs.msdn.com/joshwil/archive/2005/08/10/450202.aspx – Thorarin

+0

Gracias Thorarin, 1. Entiendo la limitación de 2 GB. Pero, ¿cómo demuestras que realmente se alcanzan los 2 GB? :-) 2. Para el 1.65G, ¿cómo se calcula ese número preciso? :-) – George2

6

Como ya se ha mencionado, este es un problema fundamental al tratar de obtener bloques contiguos de memoria en el tipo de Gigabyte.

estará limitado por (en dificultad creciente)

  1. La cantidad de memoria direccionable
    • ya que son de 64 bits esto será que 12 GB de memoria física, menos los agujeros en ella requerido por los dispositivos más cualquier espacio de archivo de intercambio.
    • Tenga en cuenta que debe ejecutar una aplicación con el relevant PE headers that indicate it can run 64bit o se ejecutará en WoW64 y solo tendrá 4 GB de espacio de direcciones.
    • También tenga en cuenta que el destino predeterminado era changed in 2010, contentious change.
  2. El CLR's limitation that no single object may consume more than 2GB of space.
  3. Encontrar un bloque contiguo dentro de la memoria disponible.

Puede encontrar que se le acaba el espacio antes del límite de CLR de 2 porque el búfer de respaldo en la corriente se expande de una manera 'doblar' y esto rápidamente se traduce en la memoria intermedia está asignada en el objeto grande Montón . Este montón no se compacta de la misma manera que los otros montones son (1) y, como resultado, el proceso de construcción hasta el tamaño máximo teórico del almacenamiento intermedio bajo 2 fragmenta el LOH para que no pueda encontrar un bloque contiguo suficientemente grande antes esto pasa.

Así, un enfoque de mitigación si está cerca al límite es establecer la capacidad inicial de la corriente de tal manera que sin duda tiene espacio suficiente desde el principio a través de one of the constructors.

Dado que está escribiendo en la secuencia de la memoria como parte de un proceso de serialización, tendría sentido utilizar las secuencias según lo previsto y usar solo los datos requeridos.

  • Si está serializando en una ubicación basada en archivos, transmítala directamente a esa ubicación.
  • Si estos son los datos de entrar en un SQL Server base de considerar el uso de:
  • Si está serializando esto en memoria para su uso en, por ejemplo una comparación y luego considerar la transmisión de los datos siendo comparado también y difiriendo a medida que avanzas.
  • Si persiste un objeto en la memoria para recrearlo más tarde, entonces esto realmente debería ir a un archivo o a un archivo mapeado en la memoria. En ambos casos, el sistema operativo es libre de estructurarlo lo mejor posible (en cachés de disco o páginas que se mapean dentro y fuera de la memoria principal) y es probable que haga un mejor trabajo de esto de lo que la mayoría de la gente puede hacer sí mismos.
  • Si está haciendo esto para que los datos se puedan comprimir, considere usar compresión de transmisión. Cualquier flujo de compresión basado en bloques se puede convertir fácilmente en un modo de transmisión con la adición de relleno. Si su API de compresión no es compatible con esto, considere usar uno que lo haga o escriba el envoltorio para hacerlo.
  • Si está haciendo esto para escribir en un búfer de bytes que luego se fija y pasa a una función no administrada, entonces use el UnmanagedMemoryStream, esto ofrece una posibilidad un poco mejor de poder asignar un búfer de este tipo de tamaño pero es todavía no está garantizado para hacerlo.

Tal vez si nos dice lo que que están serializar un objeto de este tamaño para que podría ser capaz de decirle mejores maneras de hacerlo.


  1. Este es un detalle de implementación que no debe confiar en
+0

Gracias ShuggyCoUk, su respuesta es tan genial! Tengo una pregunta relacionada aquí, aprecia si pudieras ayudar. http://stackoverflow.com/questions/1297797/windows-32-bit-virtual-memory-page-mapping-issue – George2

Cuestiones relacionadas