Byte[stream.length] - исключение из памяти, лучший способ решить?

я пытаюсь прочитать поток байтов из файла. Однако, когда я пытаюсь прочитать байты, я получаю

Оценка функции была отключена из-за исключения нехватки памяти

Довольно просто. Однако как лучше всего обойти эту проблему? Это слишком много циклов по длине 1028 за раз? Или есть лучший способ?

С#, который я использую

BinaryReader br = new BinaryReader(stream fs);

// The length is around 600000000
long Length = fs.Length;

// Error here
bytes = new byte[Length];

for (int i = 0; i < Length; i++)
{
   bytes [i] = br.ReadByte();
}

Спасибо


person user3428422    schedule 10.06.2014    source источник
comment
Вам действительно нужно иметь все данные в памяти? Каков вариант использования?   -  person ken2k    schedule 10.06.2014
comment
У вас будут очень низкие шансы на выделение 600 мегабайт в 32-битном процессе. Трудно найти такую дыру в адресном пространстве, если вы не сделаете это сразу после запуска программы. Конечно, есть лучшее решение для этого, вы не даете нам шанса угадать одно. Вам понадобится 64-битная операционная система или используйте файл с отображением памяти.   -  person Hans Passant    schedule 10.06.2014
comment
Это довольно просто: у вас закончилась память (2 ГБ), которую можно адресовать в .net. Наверное, не стоит пытаться выделить 600M в строке bytes = new byte[Length];. Что вы хотите сделать с этими данными?   -  person Dmitry Bychenko    schedule 10.06.2014
comment
Это сообщение отладчика. Это не означает, что вашему коду не хватило памяти.   -  person usr    schedule 10.06.2014
comment
Вопрос в том, как вы используете этот массив? Вам действительно нужен массив?   -  person Magnus    schedule 10.06.2014
comment
Вопрос не в этом, но читать файл побайтно, наверное, тоже не лучшее решение.   -  person Dirk    schedule 10.06.2014


Ответы (3)


Хорошо. Прежде всего. Представьте себе файл размером, например. 2 ГБ. Ваш код будет выделять 2 ГБ памяти. Просто прочитайте ту часть файла, которая вам действительно нужна, а не весь файл сразу. Во-вторых: не делайте что-то вроде этого:

for (int i = 0; i < Length; i++)
{
   bytes [i] = br.ReadByte();
}

Это довольно неэффективно. Чтобы прочитать необработанные байты потока, вы должны использовать что-то вроде этого:

using(var stream = File.OpenRead(filename))
{
    int bytesToRead = 1234;
    byte[] buffer = new byte[bytesToRead];

    int read = stream.Read(buffer, 0, buffer.Length);

    //do something with the read data ... e.g.:
    for(int i = 0; i < read; i++)
    {
        //...
    }
}
person Florian    schedule 10.06.2014
comment
Гораздо более элегантный способ, спасибо. Я пытаюсь вернуть массив byte[], чтобы я мог сохранить его в базе данных (чтобы прочитать элемент позже), возможно ли использовать приведенный выше код для этого? - person user3428422; 10.06.2014
comment
Хорошо. Я не уверен, стоит ли хранить такие большие двоичные объекты данных в базе данных. Но если вы действительно хотите это сделать, вам придется разделить данные. Прочитайте часть файла, зафиксируйте в базе данных, прочитайте следующую часть и снова зафиксируйте ее. - person Florian; 10.06.2014
comment
Да, я думал, что это может быть ответ. жаль, что вы не можете добавлять байты, но спасибо за код! - person user3428422; 10.06.2014

Когда вы пытаетесь выделить массив, CLR размещает его непрерывно в виртуальной памяти, предоставленной ему ОС. Однако виртуальная память может быть фрагментирована, поэтому непрерывный блок размером 1 ГБ может быть недоступен, отсюда и исключение OutOfMemoryException. Неважно, сколько физической оперативной памяти у вашей машины, и эта проблема не ограничивается управляемым кодом (попробуйте выделить огромный массив в родном C, и вы получите похожие результаты).

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

надеюсь, это поможет

person Moez Rebai    schedule 10.06.2014

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

Итак, почему бы не использовать данные в «br» вместо того, чтобы делать еще одну копию?

person Dario    schedule 10.06.2014
comment
BinaryReader не загружает все в память, он работает, загружая небольшие фрагменты данных, когда это необходимо. - person Bun; 10.06.2014