Объем памяти ограничен до 2,5 ГБ для одного процесса .net - PullRequest
0 голосов
/ 07 ноября 2018

Я пишу .NET приложений, работающих на Windows Server 2016, с помощью которых http получает кучу больших файлов. Это значительно ускоряет процесс загрузки, поскольку вы можете загружать их параллельно. К сожалению, после их загрузки достаточно много времени, чтобы собрать их все вместе.

Существует от 2 до 4 тыс. Файлов, которые необходимо объединить. Сервер, на котором он будет работать, имеет МНОГО памяти, близкий к 800GB. Я подумал, что имеет смысл использовать MemoryStream s для хранения загруженных фрагментов до тех пор, пока они не будут последовательно записаны на диск, НО Я смогу потреблять только 2.5GB памяти, прежде чем получить System.OutOfMemoryException ошибка. На сервере доступны сотни ГБ, и я не могу понять, как их использовать.

Ответы [ 4 ]

0 голосов
/ 10 ноября 2018

Как уже указывалось, основной проблемой здесь является характер MemoryStream, поддерживаемый byte[], который имеет фиксированный верхний размер.

Была отмечена возможность использования альтернативной реализации Stream. Другая альтернатива - заглянуть в «конвейеры», новый IO API. «Конвейер» основан на непрерывной памяти, что означает, что не требуется использовать один непрерывный буфер; библиотека конвейеров при необходимости выделит несколько блоков, которые может обработать ваш код. Я много писал на эту тему; часть 1 здесь . Часть 3, вероятно, имеет наибольшую фокусировку кода.

0 голосов
/ 07 ноября 2018

Просто чтобы подтвердить, что я понимаю ваш вопрос: вы загружаете один очень большой файл несколькими параллельными порциями, и вы знаете, насколько большой конечный файл? Если вы этого не сделаете, то это будет немного сложнее, но это все еще можно сделать.

Наилучшим вариантом, вероятно, является использование MemoryMappedFile (MMF). Что вы будете делать, это создать файл назначения через MMF. Каждый поток создаст метод доступа к этому файлу и запишет его параллельно. В конце закройте MMF. По сути, это дает вам поведение, которое вы хотели с помощью MemoryStreams, но Windows поддерживает файл на диск. Одним из преимуществ этого подхода является то, что Windows управляет хранением данных на диске в фоновом режиме (сброс), поэтому вам не нужно это делать, и это должно обеспечить превосходную производительность.

0 голосов
/ 07 ноября 2018

Согласно исходному коду MemoryStream класса вы не сможете хранить более 2 ГБ данных в одном экземпляре этого класса. Причина этого заключается в том, что максимальная длина потока установлена ​​на Int32.MaxValue, а максимальный индекс массива установлен на 0x0x7FFFFFC7, что составляет десятичное число 2,147,783,591 (= 2 ГБ).

Фрагмент MemoryStream

private const int MemStreamMaxLength = Int32.MaxValue;

Массив фрагментов

// We impose limits on maximum array lenght in each dimension to allow efficient 
// implementation of advanced range check elimination in future.
// Keep in sync with vm\gcscan.cpp and HashHelpers.MaxPrimeArrayLength.
// The constants are defined in this method: inline SIZE_T MaxArrayLength(SIZE_T componentSize) from gcscan
// We have different max sizes for arrays with elements of size 1 for backwards compatibility
internal const int MaxArrayLength = 0X7FEFFFFF;
internal const int MaxByteArrayLength = 0x7FFFFFC7;

Вопрос Более 2 ГБ управляемой памяти уже давно обсуждался на форуме Microsoft и содержит ссылку на статью в блоге о BigArray, позволяющую обойти ограничение размера массива 2 ГБ там.

Обновление

Я предлагаю использовать следующий код, который должен иметь возможность выделить более 4 ГБ в сборке x64, но потерпит неудачу <4 ГБ в сборке x86 </p>

private static void Main(string[] args)
{
    List<byte[]> data = new List<byte[]>();
    Random random = new Random();

    while (true)
    {
        try
        {
            var tmpArray = new byte[1024 * 1024];
            random.NextBytes(tmpArray);
            data.Add(tmpArray);
            Console.WriteLine($"{data.Count} MB allocated");
        }
        catch
        {
            Console.WriteLine("Further allocation failed.");
        }
    }
}
0 голосов
/ 07 ноября 2018

MemoryStreams построены вокруг байтовых массивов. В настоящее время массивы не могут быть больше 2 ГБ.

Текущая реализация System.Array использует Int32 для всех своих внутренних счетчиков и т. Д., Поэтому теоретическое максимальное количество элементов - Int32.MaxValue.

Существует также ограничение максимального размера-на-объект 2 ГБ , налагаемое Microsoft CLR.

Когда вы пытаетесь поместить содержимое в один MemoryStream, базовый массив становится слишком большим, отсюда исключение.

Попытайтесь хранить фрагменты отдельно и записать их непосредственно в FileStream (или что вы используете), когда будете готовы, без предварительной попытки объединить их все в 1 объект.

...