2012-05-03 22 views
6

Ho molti file gzip di grandi dimensioni (circa 10 MB - 200 MB) che ho scaricato da ftp per essere decompresso.decompressione GZIP C# OutOfMemory

Così ho provato a google e trovare qualche soluzione per la decompressione 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(); 
      } 
     } 
    } 

funziona bene per qualsiasi file inferiore a 50 MB ma una volta che ho inserito più di 50mb ho ottenuto l'eccezione di memoria di sistema esaurita. L'ultima posizione e la lunghezza della memoria prima dell'eccezione è 134217728. Non penso che abbia una relazione con la mia memoria fisica, capisco che non posso avere oggetti più di 2 GB poiché utilizzo 32 bit.

Ho anche bisogno di elaborare i dati dopo decomprimere i file. Non sono sicuro che il flusso di memoria sia l'approccio migliore qui, ma non mi piace scrivere sul file e poi leggere di nuovo i file.

Le mie domande

  • perché ho ricevuto System.OutMemoryException?
  • qual è la migliore soluzione possibile per decomprimere i file gzip e successivamente eseguire l'elaborazione del testo?
+0

si carica l'intero contenuto del flusso in memoria e il ritorno come matrice di byte. Cos'altro ti aspetteresti * altro * rispetto a un'eccezione di memoria insufficiente? Non dovresti caricarlo tutto in memoria come questo - cosa intendi fare in definitiva con l'array? Scrivilo in un file? Qualunque cosa tu intenda, dovrebbe essere basata sul flusso e non basata su array. –

+0

bene .. L'eccezione si verifica su memory.write e bloccato lì in 134217728 .. Non ho familiarità con la gestione della memoria, quindi per favore sopportare me. Più tardi salverò tutti i file elaborati nel database, il file all'interno dei file gzip è il file csv –

+3

Sicuro, ma il tuo design sarebbe meglio se lo elaborassi * mentre * lo stai decomprimendo. In questo modo non dovresti allocare un enorme pezzo di memoria per gestirlo. (ad esempio, lanciando il tuo flusso gzip direttamente in uno 'StreamReader') –

risposta

3

memoria la strategia di allocazione per MemoryStream non è amichevole per enormi quantità di dati.

Poiché il contratto per MemoryStream deve disporre di un array contiguo come storage sottostante, è necessario riallocare l'array abbastanza spesso per un flusso di grandi dimensioni (spesso come log2 (size_of_stream)). Gli effetti collaterali di tale riallocazione sono

  • lunghi ritardi copia su riallocazione
  • nuovo array deve inserirsi nello spazio indirizzo libero già pesantemente frammentata da precedenti attribuzioni
  • nuovo array sarà sul mucchio LOH che hanno le sue stranezze (senza compattazione, raccolta su GC2).

Come risultato la gestione di un flusso di grandi dimensioni (100Mb +) attraverso MemoryStream sarà probabilmente causa dell'eccezione di memoria sui sistemi x86. Inoltre modello più comune per restituire dati è chiamare GetArray come si fa che richiede inoltre circa la stessa quantità di spazio come buffer ultimo matrice utilizzata per MemoryStream.

approcci per risolvere:

  • Il modo più economico è quello di pre-crescita MemoryStream, sul ravvicinamento formato che avete bisogno (preferibilmente leggermente grande). È possibile pre-elaborazione dimensione che è richiesto da leggere per flusso di falso che non memorizza nulla (spreco di risorse della CPU, ma sarete in grado di leggerlo). Considera anche il ritorno del flusso anziché l'array di byte (o l'array di byte di ritorno del buffer MemoryStream insieme alla lunghezza).
  • Un'altra opzione per gestire la cosa se avete bisogno di tutto il flusso o array di byte è quello di utilizzare flusso di file temporanei, invece di MemoryStream per memorizzare grandi quantità di dati.
  • approccio più complesso è quello di implementare flusso che blocchi di dati sottostanti in piccole (64K) cioè blocchi di evitare un'assegnazione su LOH e dati copia quando flusso deve crescere.
+0

Sì, grazie per chiarirmi questo. In un certo senso ora capisco, il flusso di memoria non era un buon amico per me in questo caso. Ho pensato che potesse aiutare più velocemente le prestazioni, ma invece mi dà più mal di testa. Grazie –

0

capisco che non posso avere oggetto più di 2GB dato che io uso a 32 bit

Questo non è corretto. Puoi avere tutta la memoria di cui hai bisogno. Limitazione a 32 bit significa che è possibile avere solo 4 GB (il sistema operativo ne prende la metà) in Spazio indirizzo virtuale. Lo spazio degli indirizzi virtuali non è memoria. Here è una bella lettura.

perché ho ricevuto System.OutMemoryException?

Poiché allocator non è riuscito a trovare lo spazio di indirizzamento contiguo per il proprio oggetto o si verifica troppo rapidamente e si ostruisce. (Molto probabilmente il primo)

Qual è la migliore soluzione possibile per decomprimere i file gzip e dopo l'elaborazione del testo è possibile eseguire ?

Scrivere uno script che scarica i file, quindi utilizza strumenti come gzip o 7zip per decomprimerlo e quindi elaborarlo. A seconda del tipo di elaborazione, dei numeri di file e della dimensione totale, sarà necessario salvarli a un certo punto per evitare problemi di memoria di questo tipo. Salvali dopo aver decompresso e processato 1 MB in una sola volta.

+5

[L'OP è corretto in merito al limite della dimensione dell'array * da 2 GB * (http: // StackOverflow.com/domande/1087982/singoli-oggetti-ancora-limitato-to-2-it-in-formato-in-clr-4-0). Inoltre, penso che suggerire uno strumento esterno come 7-zip manchi completamente lo spirito di questa ricerca. –

1

Si può provare un test simile al seguente per avere un'idea di quanto si può scrivere a MemoryStream prima di ottenere 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"); 
     } 

Potrebbe essere necessario decomprimere un file fisica temporanea prima e ri- leggetelo in piccoli pezzi e procedete mentre procedete.

Point laterale: è interessante notare che, su un PC con Windows XP, il codice di cui sopra dà: "OutOfMemory intorno dimensione 256MB" quando gli obiettivi NET 2.0, e "OutOfMemory intorno dimensioni 512" su .NET 4.

+1

Ho già specificato sopra. È stato bloccato su 134217728 all'incirca su 128 MB se ho corretto. Non sono sicuro del motivo per cui ciò accade troppo presto, ma credo che scegliere il flusso di memoria sia il mio primo errore .. Grazie per la tua risposta –

+0

Posso confermare che ho raggiunto lo stesso limite ESATTIVO. – Kris

1

Ti capita di elaborare i file in più thread? Ciò consumerebbe una grande quantità di spazio degli indirizzi. Gli errori OutOfMemory di solito non sono correlati alla memoria fisica e pertanto MemoryStream può esaurirsi molto prima di quanto ci si aspetti. Controlla questa discussione http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/1af59645-cdef-46a9-9eb1-616661babf90. Se passassi a un processo a 64 bit, probabilmente sarai più che OK per le dimensioni dei file con cui hai a che fare.

Nella tua situazione attuale, però, si potrebbe lavorare con i file mappati in memoria per aggirare eventuali limiti di dimensione indirizzo. Se si utilizza .NET 4.0, fornisce un wrapper nativo per le funzioni di Windows http://msdn.microsoft.com/en-us/library/dd267535.aspx.

+0

Sì, ho visto quel link prima di chiederlo a SO. Voglio solo sapere quali altre opzioni ho. Grazie per la tua risposta –