OutOfMemoryException в C # - PullRequest
       2

OutOfMemoryException в C #

1 голос
/ 21 февраля 2012

Этот код вызывает утечку памяти.Я предполагаю, что это вызвано new byte[].Но не должен ли GC избежать этого?Если программа работает достаточно долго, код вызовет исключение OutOfMemoryException

using (var file = new FileStream(fileLoc, FileMode.Open))
{
    int chunkSize = 1024 * 100;
    while (file.Position < file.Length)
    {
        if (file.Length - file.Position < chunkSize)
        {
            chunkSize = (int)(file.Length - file.Position);
        }
        byte[] chunk = new byte[chunkSize];
        file.Read(chunk, 0, chunkSize);
        context.Response.BinaryWrite(chunk);
    }
}

Ответы [ 3 ]

6 голосов
/ 21 февраля 2012

Проблема почти наверняка в том, что вы неоднократно выделяете новые массивы, а в памяти они выделяются как непрерывные блоки, поэтому я могу понять, как он пережевывает их.

Как насчет немного перенастроить вещи, чтобы создать буфер только один раз, а затем использовать его повторно, если только вы не попадете в if, если требуемый размер фрагмента меньше стандартного размера фрагмента.

using (var file = new FileStream(fileLoc, FileMode.Open)) {
    int chunkSize = 1024 * 100;
    byte[] chunk = new byte[chunkSize];

    while (file.Position < file.Length) {
        if (file.Length - file.Position < chunkSize) {
            chunkSize = (int)(file.Length - file.Position);
            chunk = new byte[chunkSize];
        }
        file.Read(chunk, 0, chunkSize);
        context.Response.BinaryWrite(chunk);
    } 
}
2 голосов
/ 21 февраля 2012

Могу ли я предложить вам попробовать с меньшим размером буфера?

Проблема может заключаться в том, что вы неоднократно выделяете блок памяти, размер которого превышает 85000 байт, и который попадает в специальную кучу (кучу больших объектов), которая, к сожалению, никогда не сжимается!

См. здесь для подробного объяснения того, как работает куча больших объектов. К сожалению, это может привести к серьезной фрагментации кучи и в конечном итоге вызвать ошибку нехватки памяти, подобную той, которую вы описываете (см. Здесь: фрагментация loh вызывает исключение OutOfMemory )

Если вы выделите более мелкие куски (менее 85 000 байтов), тогда они будут размещены в обычной куче, тогда сборщик мусора сможет выполнить сжатие и почти наверняка ваша проблема исчезнет. Я бы также настоятельно рекомендовал вам изменить ваш код, как предложено @Nanhydrin, так как это позволяет избежать повторного выделения и должно работать немного лучше

1 голос
/ 21 февраля 2012

Я не совсем уверен, что вы подразумеваете под "достаточно долго", однако этот код выделяет массив размером не менее 100 КБ (возможно, больше, если файл больше).Само по себе это, вероятно, не вызовет сбоя, однако в среде с 32 МБ виртуального адресного пространства это достаточно большой объем памяти.Если многие из них работают параллельно, это может легко умножить это до относительно высокого использования памяти, в этом случае вы можете увидеть OutOfMemoryException.

С предположением, что context.Response является HttpResponse похоже, что вы просто пытаетесь записать содержимое файла в ответ HTTP, и в этом случае вы можете сделать это гораздо эффективнее, используя что-то вроде следующего:

using (var file = new FileStream(fileLoc, FileMode.Open))
{
    CopyStream(file, context.Response.OutputStream);
}

См. Лучший способ копирования между двумя экземплярами Stream - C # для реализации CopyStream, которая копирует данные побитно за меньшие порции, а не пытается прочитать весь файл за один раз.

...