2012-05-03 6 views
6

У меня есть много больших файлов gzip (приблизительно 10 МБ - 200 МБ), которые я скачал из ftp для распаковки.GZIP декомпрессии C# OutOfMemory

Итак, я попытался найти Google и найти решение для декомпрессии 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(); 
      } 
     } 
    } 

он хорошо работает для любых файлов ниже 50 МБ, но как только у меня есть ввод более 50 МБ, я получил исключение из памяти. Последняя позиция и длина памяти до исключения - 134217728. Я не думаю, что она имеет отношение к моей физической памяти, я понимаю, что у меня не может быть объекта более 2 ГБ, поскольку я использую 32-разрядную.

Мне также необходимо обработать данные после распаковки файлов. Я не уверен, что здесь лучше всего использовать поток памяти, но мне не нравится писать в файл, а затем снова читать файлы.

Мои вопросы

  • почему я System.OutMemoryException?
  • Что такое наилучшее возможное решение для распаковки файлов gzip и последующей обработки текста?
+0

Вы загружаете все содержимое потока в память и возвращаете его как массив байтов. Что еще вы ожидаете * other *, чем исключение из памяти? Вы не должны загружать все это в память так: что вы в конечном итоге собираетесь делать с массивом? Напишите его в файл? Что бы вы ни предполагали, он должен быть основан на потоках, а не на массиве. –

+0

ну .. Исключение происходит на memory.write и застревает там в 134217728 .. Я не знаком с управлением памятью, поэтому, пожалуйста, несите меня. Позже я сохраню все обработанные файлы в базе данных, файл внутри gzipped-файлов - файл csv –

+3

Конечно, но ваш дизайн был бы лучше, если бы вы его обработали * пока * вы разархивируете его. Таким образом вам не нужно было бы выделять огромный кусок памяти для его обработки. (например, бросая ваш поток gzip непосредственно в 'StreamReader') –

ответ

3

памяти стратегия распределения для MemoryStream не подходит для огромного количества данных.

Поскольку контракт для MemoryStream должен иметь непрерывный массив в качестве базового хранилища, он должен перераспределять массив достаточно часто для большого потока (часто как log2 (size_of_stream)). Побочные эффекты такого перераспределения являются

  • длительные задержки копирования на перераспределении
  • новый массив должен вписываться в свободном адресном пространстве уже сильно фрагментированы предыдущими ассигнованиями
  • новый массив будет на LOH куче, которые имеют свои особенности (нет уплотнение, сбор на GC2).

В результате обработка большого (100 Мб +) потока через MemoryStream, скорее всего, исключает исключение из памяти в системах x86. Кроме того, наиболее распространенным шаблоном для возврата данных является вызов GetArray, как вы это делаете, что дополнительно требует примерно того же объема пространства, что и последний буфер массива, используемый для MemoryStream.

Подходы к решению:

  • Самый дешевый способ предварительно расти MemoryStream аппроксимировать размер вам (желательно немного большим). Вы можете предварительно вычислить размер, необходимый для чтения, чтобы поддельный поток, который ничего не хранит (отходы ресурсов ЦП, но вы сможете его прочитать). Рассмотрим также возврат потока вместо байтового массива (или возвращаемого байтового массива буфера MemoryStream вместе с длиной).
  • Другим вариантом обработки, если вам нужен целый поток или массив байтов, является использование временного файлового потока вместо MemoryStream для хранения большого количества данных.
  • Более сложный подход заключается в реализации потока, который разбивает базовые данные в меньших (то есть 64K) блоках, чтобы избежать выделения на LOH и копирования данных, когда поток должен расти.
+0

Да, спасибо, поясните это мне. Я сейчас понимаю, поток памяти для меня не был хорошим другом. Я думал, что это может ускорить производительность, но вместо этого это дает мне больше головной боли. Спасибо –

0

Я понимаю, что я не могу иметь объект больше, чем 2 Гб, поскольку я использую 32-битную

Это неправильно. У вас может быть столько памяти, сколько вам нужно. 32-битное ограничение означает, что вы можете иметь только 4 ГБ (ОС занимает половину) виртуального адресного пространства. Виртуальное адресное пространство не является памятью. Here приятно читать.

Почему я получил System.OutMemoryException?

Поскольку распределитель не смог найти смежного адресного пространства для вашего объекта, или это происходит слишком быстро, и он забивается. (Скорее всего, первые один)

что является наилучшим решением для распаковки GZIP файлов и делать какую-нибудь текст обработку после этого?

Напишите сценарий, который загружает файлы, а затем использует такие инструменты, как gzip или 7zip, чтобы распаковать его, а затем обработать. В зависимости от вида обработки, количества файлов и общего размера вам придется в какой-то момент сохранить их, чтобы избежать подобных проблем с памятью. Сохраните их после распаковки и обработайте 1 МБ сразу.

+5

[OP правильна в отношении размера массива размером 2 ГБ *) (http: // stackoverflow.ком/вопросы/1087982/одиночные-объекты-прежнему ограниченные-к-2-GB-в-размер-в-CLR-4-0). Кроме того, я думаю, что предлагая внешний инструмент, такой как 7-zip, полностью упускает дух этого квеста. –

1

Вы можете попробовать тест, как следующее, чтобы почувствовать, как много вы можете написать MemoryStream прежде, чем получить 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"); 
     } 

Вы, возможно, придется распаковать во временный физический файл первого и повторно читать его в небольших кусках и обрабатывать, как вы идете.

Side Точка: интересно, на XP Windows ПК, приведенный выше код дает: «OutOfMemory вокруг размера 256», когда код цели .net 2.0, и «OutOfMemory вокруг размера 512» на .net 4.

+1

Я уже указывал выше. Он застрял на 134217728 примерно 128 МБ, если я исправлю. Я не уверен, почему это происходит слишком рано, но я думаю, что выбор потока памяти - это моя первая ошибка. Спасибо за ваш ответ. –

+0

Могу подтвердить, что я ударил ТОЧНЫЙ тот же предел. – Kris

1

Возможно, вы работаете с файлами в нескольких потоках? Это потребует большого количества вашего адресного пространства. Ошибки OutOfMemory обычно не связаны с физической памятью, поэтому MemoryStream может работать намного раньше, чем вы ожидали. Проверьте это обсуждение http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/1af59645-cdef-46a9-9eb1-616661babf90. Если вы перейдете на 64-битный процесс, вы, вероятно, будете более чем ОК для размеров файлов, с которыми имеете дело.

В вашей текущей ситуации, однако, вы можете работать с файлами с отображением памяти, чтобы обойти любые ограничения по размеру адреса. Если вы используете .NET 4.0, он предоставляет встроенную оболочку для функций Windows http://msdn.microsoft.com/en-us/library/dd267535.aspx.

+0

Да, я видел эту ссылку, прежде чем спросить в SO. Я просто хочу знать, какие у меня есть другие варианты. Спасибо, что ответили –

 Смежные вопросы

  • Нет связанных вопросов^_^