2012-05-03 27 views
6

Tengo muchos archivos grandes gzip (aproximadamente 10MB - 200MB) que descargué de ftp para descomprimir.descompresión GZIP C# OutOfMemory

Intenté buscar en Google y encontrar alguna solución para la descompresión de gzip.

static byte[] Decompress(byte[] gzip) 
    { 
     using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress)) 
     { 
      const int size = 4096; 
      byte[] buffer = new byte[size]; 
      using (MemoryStream memory = new MemoryStream()) 
      { 
       int count = 0; 
       do 
       { 
        count = stream.Read(buffer, 0, size); 
        if (count > 0) 
        { 
         memory.Write(buffer, 0, count); 
        } 
       } 
       while (count > 0); 
       return memory.ToArray(); 
      } 
     } 
    } 

que funciona bien para los archivos por debajo de 50 MB, pero una vez que tengo más de 50 MB de entrada que tiene el sistema de excepción de memoria. Última posición y la longitud de la memoria antes de la excepción es 134217728. No creo que tenga relación con mi memoria física, entiendo que no puedo tener objetos de más de 2GB ya que uso 32 bits.

También necesito procesar los datos después de descomprimir los archivos. No estoy seguro de si el flujo de memoria es el mejor enfoque aquí, pero realmente no me gusta escribir en el archivo y luego leer los archivos de nuevo.

Mis preguntas

  • ¿Por qué me System.OutMemoryException?
  • ¿cuál es la mejor solución posible para descomprimir archivos gzip y hacer algo de procesamiento de texto después?
+0

Está cargando todo el contenido de la secuencia en la memoria y devolviéndola como una matriz de bytes. ¿Qué otra cosa esperarías * otra * excepción de una memoria insuficiente? No debería cargar todo en la memoria de esta manera: ¿qué pretende hacer con la matriz en última instancia? Escribir en un archivo? Lo que sea que intentes, debe estar basado en secuencias y no en una matriz. –

+0

bien ... La excepción se produce en memory.write y se queda allí en 134217728 .. No estoy familiarizado con la administración de la memoria, así que por favor tengan paciencia conmigo. Más tarde guardaré todos los archivos procesados ​​en la base de datos, el archivo dentro de los archivos comprimidos es el archivo csv –

+3

Claro, pero su diseño sería mejor si lo procesara * mientras * lo está descomprimiendo. De esa manera no tendrías que asignar una gran cantidad de memoria para manejarlo. (por ejemplo, lanzando su flujo gzip directamente en un '' StreamReader'') –

Respuesta

3

Memoria estrategia de asignación para MemoryStream no es amigable para grandes cantidades de datos.

Dado que el contrato para MemoryStream es tener una matriz contigua como almacenamiento subyacente, tiene que reasignar la matriz con la frecuencia suficiente para la transmisión de gran tamaño (a menudo como log2 (size_of_stream)). Los efectos secundarios de dicha reasignación son

  • largos retrasos de copia en la reasignación
  • nueva matriz debe encajar en el espacio libre de direcciones ya fuertemente fragmentado por las asignaciones anteriores
  • nueva matriz estará en el montón LOH que tiene sus peculiaridades (sin compactación, recolección en GC2).

Como resultado, el manejo de una transmisión de gran tamaño (100Mb +) a través de MemoryStream probablemente sea una excepción de falta de memoria en los sistemas x86. Además, el patrón más común para devolver datos es llamar a GetArray como lo hace, que además requiere aproximadamente la misma cantidad de espacio que el último búfer de matriz utilizado para MemoryStream.

enfoques para resolver:

  • La manera más barata es comprobar la validez de crecer MemoryStream para aproximar el tamaño que necesita (de preferencia ligeramente grande). Puede precalcular el tamaño que se requiere leyendo flujo falso que no almacena nada (desperdicio de recursos de CPU, pero podrá leerlo). Considere también devolver la secuencia en lugar de la matriz de bytes (o devolver la matriz de bytes del búfer MemoryStream junto con la longitud).
  • Otra opción para manejarlo si necesita una matriz completa o una matriz de bytes es usar una secuencia de archivos temporales en lugar de MemoryStream para almacenar una gran cantidad de datos.
  • El enfoque más complicado es implementar un flujo que fragmente los datos subyacentes en bloques más pequeños (es decir, 64 K) para evitar la asignación en LOH y copiar datos cuando el flujo necesita crecer.
+0

Sí, gracias por aclararme esto. Ahora comprendo que el flujo de memoria no fue un buen amigo para mí en este caso. Pensé que podría ayudar a acelerar el rendimiento, pero en cambio me da más dolor de cabeza. Gracias –

0

entiendo que no puedo tener objeto más de 2 GB ya que utilizar 32 bits

Eso es incorrecto. Puedes tener tanta memoria como necesites. La limitación de 32 bits significa que solo puede tener 4 GB (el SO toma la mitad) del espacio de direcciones virtuales. El espacio de direcciones virtuales no es memoria. Here es una buena lectura.

¿por qué recibí System.OutMemoryException?

Dado que el asignador no ha podido encontrar el espacio de direcciones contiguo para su objeto o pasa demasiado rápido y se obstruye. (Lo más probable es el primero)

¿cuál es la mejor solución posible para descomprimir los archivos gzip y hacer algún tipo de procesamiento de texto después?

Escriba un script que descargue los archivos, luego use herramientas como gzip o 7zip para descomprimirlo y luego procesarlo. Según el tipo de procesamiento, el número de archivos y el tamaño total, deberá guardarlos en algún momento para evitar este tipo de problemas de memoria. Guárdelos después de descomprimir y procesar 1MB a la vez.

+5

[El OP es correcto sobre el límite de 2GB * de tamaño de matriz *] (http: // stackoverflow.com/questions/1087982/single-objects-still-limited-to-2-gb-in-size-in-clr-4-0). Además, creo que sugerir una herramienta externa como 7-zip echa de menos por completo el espíritu de esta queston. –

1

Usted puede tratar de una prueba como la siguiente para tener una idea de cuánto puede escribir en MemoryStream antes de conseguir un OutOfMemoryException:

 const int bufferSize = 4096; 
     byte[] buffer = new byte[bufferSize]; 

     int fileSize = 1000 * 1024 * 1024; 

     int total = 0; 

     try 
     { 
      using (MemoryStream memory = new MemoryStream()) 
      { 
       while (total < fileSize) 
       { 
        memory.Write(buffer, 0, bufferSize); 
        total += bufferSize; 
       } 

      } 

      MessageBox.Show("No errors"); 

     } 
     catch (OutOfMemoryException) 
     { 
      MessageBox.Show("OutOfMemory around size : " + (total/(1024m * 1024.0m)) + "MB"); 
     } 

Usted puede tener que descomprimir un archivo físico temporal primero y volver léalo en trozos pequeños y procese a medida que avanza.

Punto lateral: curiosamente, en un PC con Windows XP, el código anterior da: "OutOfMemory alrededor del tamaño de 256 MB" cuando los objetivos de código .NET 2.0, y "OutOfMemory alrededor del tamaño de 512 MB" en .NET 4.

+1

Ya especifiqué arriba. Se atascó en 134217728 aproximadamente 128 MB si lo corrijo. No estoy seguro de por qué sucede esto demasiado pronto, pero supongo que elegir el flujo de memoria es mi primer error. Gracias por su respuesta –

+0

Puedo confirmar que he alcanzado el mismo límite EXACTO. – Kris

1

¿Está procesando archivos en varios subprocesos? Eso consumiría una gran cantidad de espacio de direcciones. Los errores de OutOfMemory generalmente no están relacionados con la memoria física, por lo que MemoryStream puede agotarse mucho antes de lo esperado. Consulte esta discusión http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/1af59645-cdef-46a9-9eb1-616661babf90. Si cambiaste a un proceso de 64 bits, probablemente estarías más que de acuerdo con los tamaños de archivo con los que estás lidiando.

Sin embargo, en su situación actual, podría trabajar con archivos mapeados en memoria para evitar los límites de tamaño de la dirección. Si está utilizando .NET 4.0, proporciona un contenedor nativo para las funciones de Windows http://msdn.microsoft.com/en-us/library/dd267535.aspx.

+0

Sí, vi ese enlace antes de preguntar en SO. Solo quiero saber qué otras opciones tengo. Gracias por tu respuesta –