Возможные причины для FileStream.Write (), чтобы вызвать исключение OutOfMemoryException? - PullRequest
3 голосов
/ 16 февраля 2009

У меня есть 10 потоков, записывающих тысячи маленьких буферов (16-30 байт каждый) в огромный файл в случайных позициях. Некоторые потоки генерируют исключение OutOfMemoryException для операции FileStream.Write ().

Что вызывает исключение OutOfMemoryException? Что искать?

Я использую FileStream следующим образом (для каждого записанного элемента - этот код выполняется из 10 различных потоков):

using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, BigBufferSizeInBytes, FileOptions.SequentialScan))
{
 ...
 fs.Write();
}

Я подозреваю, что все буферы, выделенные внутри FileStream, не освобождаются GC вовремя. Чего я не понимаю, так это почему CLR вместо броска не просто запускает цикл GC и освобождает все неиспользуемые буферы?

Ответы [ 5 ]

2 голосов
/ 16 февраля 2009

Если десять потоков открывают файлы, как показывает ваш код, то у вас одновременно может быть не более десяти неразмещенных объектов FileStream. Да, FileStream имеет внутренний буфер, размер которого вы указываете с помощью «BigBufferSizeInBytes» в своем коде. Не могли бы вы раскрыть точное значение? Если он достаточно большой (например, ~ 100 МБ), то это может быть источником проблемы.

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

Однако для вашего конкретного приложения это не имеет особого смысла, так как указанный буфер никогда не будет содержать больше 16-30 байт, которые вы записываете в него до того, как Dispose () объект FileStream.

Чтобы ответить на ваш вопрос, исключение OutOfMemoryException генерируется только тогда, когда запрошенная память не может быть выделена после запуска ГХ. Опять же, если буфер действительно большой, то в системе может остаться достаточно памяти, но не просто непрерывный блок. Это связано с тем, что куча больших объектов никогда не сжимается.

1 голос
/ 15 мая 2009

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

Я сталкивался с этой проблемой довольно часто, когда делал почти точно то, что вы описали здесь.

Вам нужно опубликовать больше кода, чтобы правильно ответить на этот вопрос. Тем не менее, я предполагаю, что это также может быть связано с потенциальной проблемой Хэллоуина (Spooky Dooky) .

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

OOP! просто прокрутил и заметил "BigBufferSizeInBytes", я снова склоняюсь к куче больших объектов ...

Если бы я был вами (а это чрезвычайно сложно из-за отсутствия контекста), я бы предоставил небольшую рассылку "mbuf", куда вы копировали вход и выход, вместо того, чтобы все ваши отключенные потоки могли по отдельности прочитать ваш большой резервный массив ... (то есть трудно не вызвать неадекватное распределение с очень тонким синтаксисом кода).

0 голосов
/ 24 июня 2009

Я испытываю что-то подобное и спрашиваю себя, не связали ли вы когда-нибудь корень своей проблемы?

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

Я отследил проблему до вызова FileStream.Write () - когда эта строка удалена, использование памяти, кажется, идет как ожидалось. Мой BigBufferSizeInBytes - это значение по умолчанию (4 КБ), и я нигде не вижу, где они могут собираться ...

Все, что вы обнаружили во время рассмотрения вашей проблемы, будет с благодарностью получено!

0 голосов
/ 16 февраля 2009

Возможно, что эти ограничения возникают из-за базовой ОС и что .NET Framework не в силах преодолеть такого рода ограничения.

Что я не могу вывести из вашего примера кода, так это то, открываете ли вы одновременно много объектов FileStream или открываете их очень быстро по очереди. Использование ключевого слова using позволяет убедиться, что файлы закрываются после вызова fs.Write (). Для закрытия файла цикл GC не требуется.

Класс FileStream действительно ориентирован на последовательный доступ для чтения / записи к файлам. Если вам нужно быстро выполнить запись в случайные места в большом файле, вам может понадобиться использовать виртуальное сопоставление файлов.

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

Dave

0 голосов
/ 16 февраля 2009

Буферы обычно не выделяются внутри FileStream. Возможно, проблема в строке «запись тысяч маленьких буферов» - вы действительно это имеете в виду? Обычно вы повторно используете буфер много, много, много раз (т.е. при разных вызовах для чтения / записи).

Также - это один файл? Один FileStream не гарантированно безопасен для потоков ... поэтому, если вы не выполняете синхронизацию, ожидайте хаоса.

...