Системная ошибка outofmemory: недостаточно памяти

Обновлено: 02.07.2024

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

Веб-разработка, языки программирования, тестирование программного обеспечения и другое

Синтаксис

try
//код пользователя, который может генерировать OutOfMemoryException
>
catch(OutOfMemoryException exception)
// операторы для обработки исключения
> />>

генерировать новое исключение OutOfMemoryException();

Попытка увеличить длину объекта StringBuilder сверх длины, указанной в свойстве MaxCapacity объекта StringBuilder.

Мы получим исключение "Недостаточно памяти для продолжения выполнения программы".

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

Другие причины, которые могут стать причиной этого исключения, включают:

  • Запуск приложения в 32-разрядной системе, имеющей всего 2 ГБ виртуальной памяти, из-за которой CLR трудно выделить непрерывную память для выделения, требуемого приложению.
  • После работы с неуправляемыми ресурсами, такими как обработчики файлов, подключения к базе данных, указатели и т. д., если мы не избавимся от этих ресурсов, это приведет к утечке памяти, что в результате снизит производительность нашего приложения и может привести к OutOfMemoryException.< /li>
  • Для работы с большими наборами данных требуется огромный объем памяти, и если в среде CLR недостаточно свободного непрерывного пространства, возникает исключение OutOfMemoryException.
  • Поскольку строки неизменяемы, операции, выполняемые над строкой, создают новую строку в памяти. Поэтому, если мы работаем с большими строками и многократно выполняем операцию конкатенации строки, это может привести к многократному выделению памяти, что в результате снизит производительность нашего приложения и может стать причиной OutOfMemoryException.
  • Если мы закрепили несколько объектов в памяти на очень длительный период времени, сборщику мусора становится трудно обеспечить непрерывное выделение памяти для этих объектов.

Примеры

Вот следующие примеры, упомянутые ниже

Пример, показывающий OutOfMemoryException, созданный программой, когда мы пытаемся расширить объект StringBuilder сверх его максимальной емкости.

Код:

Вывод:

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

Код:

используя Систему;
с помощью System.Text;
использование System.Collections.Generic;
пространство имен ConsoleApp4
общедоступный класс Program
public static void Main()
try
string[] strArray = GetArray();
Console.WriteLine(strArray);
Console.ReadLine();
>
catch (исключение OutOfMemoryException)
Console.WriteLine(Exception);
Console.ReadLine();
>
catch (Exception ex)
Console.WriteLine(ex.Message);
Console.ReadLine();
>
>
public static string[] GetArray()
List strList = new List ();
для (целое я = 0; я

Вывод:

  • Чтобы избежать этого исключения при работе с StringBuilder, мы можем вызвать конструктор StringBuilder.StringBuilder(Int32, Int32) и установить для свойства MaxCapacity значение, которое будет достаточно большим, чтобы обеспечить необходимое согласование при развертывании соответствующего StringBuilder. объект.
  • Чтобы избежать этого исключения при работе в 32-разрядной системе, мы можем перекомпилировать наше приложение из 32-разрядной системы в 64-разрядную в Visual Studio, выполнив следующие действия:
  1. Строка меню -> Сборка -> Диспетчер конфигурации
  2. Нажмите в списке Active Solution Platform, выберите 64-разрядную платформу и нажмите кнопку "Закрыть".

Если 64-разрядная платформа отсутствует в списке, то:

  • Нажмите «Новый» в списке.
  • В окне "Новая платформа решения" щелкните список "Введите или выберите новую платформу", а затем выберите вариант "x64".
  • Нажмите кнопку "ОК".
  • Чтобы избежать возникновения этого исключения при работе с неуправляемыми ресурсами, мы всегда должны вызывать метод Dispose() после завершения работы с неуправляемым ресурсом, который больше не требуется.
  • Чтобы избежать этого исключения при работе с большими наборами данных, мы должны попытаться отфильтровать данные, а затем передавать только необходимые данные для обработки.
  • Чтобы избежать этого исключения при работе с большими строками или при выполнении конкатенации больших строк, мы можем использовать StringBuilder вместо строки, поскольку StringBuilder является изменяемым и не создает новый экземпляр строки, когда мы выполняем над ней какую-либо операцию.

Заключение

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

Рекомендуемые статьи

Исключение: возникло исключение типа «System.OutOfMemoryException».

У меня 4 ГБ памяти на этой машине. 2,5 ГБ свободно, когда я запускаю эту программу, на ПК явно достаточно места для обработки 762 МБ 100000000 случайных чисел. Мне нужно сохранить как можно больше случайных чисел, учитывая доступную память. Когда я приступлю к производству, на коробке будет 12 ГБ, и я хочу использовать это.

Ограничивает ли CLR максимальный объем памяти по умолчанию? и как мне запросить больше?

Обновить

Я думал, что разделение этого на более мелкие фрагменты и постепенное добавление к моим требованиям к памяти поможет, если проблема связана с фрагментацией памяти, но это не так. Я не могу преодолеть общий размер ArrayList 256 МБ, независимо от того, что я делаю. настройка размера блока.

Из моего основного метода:

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

@bosit это сайт вопросов и ответов. Если у вас есть конкретный технический вопрос о фактическом коде, опубликуйте его как вопрос.

14 ответов 14

Короче и очень упрощенно, "Недостаточно памяти" на самом деле не означает, что объем доступной памяти слишком мал. Наиболее распространенная причина заключается в том, что в текущем адресном пространстве нет непрерывной части памяти, которая была бы достаточно большой для обслуживания требуемого распределения. Если у вас есть 100 блоков по 4 МБ каждый, это не поможет вам, когда вам нужен один блок по 5 МБ.

Ключевые моменты:

  • хранилище данных, которое мы называем «памятью процесса», на мой взгляд, лучше всего представить в виде массивного файла на диске.
  • Оперативную память можно рассматривать просто как средство оптимизации производительности.
  • Общий объем виртуальной памяти, потребляемой вашей программой, на самом деле не имеет большого отношения к ее производительности.
  • "недостаточно оперативной памяти" редко приводит к ошибке "недостаточно памяти". Вместо ошибки это приводит к снижению производительности, поскольку внезапно становится актуальной полная стоимость того факта, что хранилище фактически находится на диске.

"Если у вас есть 100 блоков, каждый размером 4 МБ, это не поможет вам, когда вам нужен один блок размером 5 МБ" — я думаю, лучше было бы сформулировать это с небольшой поправкой: "Если у вас есть 100 "дырочных" блоков".

Убедитесь, что вы создаете 64-разрядный процесс, а не 32-разрядный, который является режимом компиляции Visual Studio по умолчанию. Для этого щелкните правой кнопкой мыши свой проект, Свойства -> Сборка -> Целевая платформа: x64. Как и любой 32-разрядный процесс, приложения Visual Studio, скомпилированные в 32-разрядной версии, имеют ограничение виртуальной памяти в 2 ГБ.

64-битные процессы не имеют этого ограничения, так как они используют 64-битные указатели, поэтому их теоретическое максимальное адресное пространство (размер их виртуальной памяти) составляет 16 эксабайт (2^64). На самом деле Windows x64 ограничивает виртуальную память процессов до 8 ТБ. Тогда решение проблемы ограничения памяти заключается в компиляции в 64-разрядной версии.

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

  1. Вы можете попробовать работать с /3GB (как предлагали другие)
  2. Или переключитесь на 64-разрядную ОС.
  3. Или измените алгоритм, чтобы ему не требовался большой объем памяти. возможно, выделить несколько меньших (относительно) кусков памяти.

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

Затем, чтобы получить конкретный индекс, вы должны использовать randomNumbers[i / sizeB][i % sizeB] .

Другим вариантом, если вы всегда обращаетесь к значениям по порядку, может быть использование перегруженного конструктора для указания начального значения. Таким образом, вы получите полуслучайное число (например, DateTime.Now.Ticks ), сохраните его в переменной, а затем, когда вы начнете просматривать список, вы создадите новый экземпляр Random, используя исходное семя:

Важно отметить, что хотя в блоге, указанном в ответе Фредрика Мёрка, указывается, что проблема обычно возникает из-за нехватки адресного пространства, в нем не указан ряд других проблем, таких как 2 ГБ. Ограничение размера объекта CLR (упомянутое в комментарии ShuggyCoUk в том же блоге), замалчивается фрагментация памяти и не упоминается влияние размера файла подкачки (и то, как его можно устранить с помощью функции CreateFileMapping).

Фрагментация памяти очень похожа на фрагментацию жесткого диска. У вас может быть 2 ГБ адресного пространства, но при создании и уничтожении объектов между значениями будут пробелы. Если эти промежутки слишком малы для вашего большого объекта, и дополнительное пространство не может быть запрошено, вы получите исключение System.OutOfMemoryException. Например, если вы создаете 2 миллиона объектов размером 1024 байта, вы используете 1,9 ГБ. Если вы удалите каждый объект, адрес которого не кратен 3, то вы будете использовать 0,6 ГБ памяти, но она будет распределена по адресному пространству с открытыми блоками по 2024 байта между ними. Если вам нужно создать объект размером 0,2 ГБ, вы не сможете это сделать, потому что нет достаточно большого блока, чтобы поместить его, и дополнительное пространство не может быть получено (при условии 32-битной среды). Возможными решениями этой проблемы являются такие вещи, как использование объектов меньшего размера, уменьшение объема данных, которые вы храните в памяти, или использование алгоритма управления памятью для ограничения/предотвращения фрагментации памяти. Следует отметить, что если вы не разрабатываете большую программу, использующую большой объем памяти, это не будет проблемой. Кроме того, эта проблема может возникнуть в 64-разрядных системах, так как окна в основном ограничены размером файла подкачки и объемом оперативной памяти в системе.

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

Исключение, возникающее, когда недостаточно памяти для продолжения выполнения программы.

Примечания

OutOfMemoryException использует HRESULT COR_E_OUTOFMEMORY со значением 0x8007000E.

Список начальных значений свойств для экземпляра OutOfMemoryException см. в конструкторах OutOfMemoryException.

Значение унаследованного свойства Data всегда равно null .

Исключение OutOfMemoryException возникает по двум основным причинам:

Вы пытаетесь расширить объект StringBuilder за пределы длины, определенной его свойством StringBuilder.MaxCapacity.

Среда CLR не может выделить достаточно непрерывной памяти для успешного выполнения операции. Это исключение может быть вызвано любым назначением свойства или вызовом метода, требующим выделения памяти. Дополнительную информацию о причине исключения OutOfMemoryException см. в записи блога «Недостаточно памяти» не относится к физической памяти.

Этот тип исключения OutOfMemoryException представляет собой катастрофический сбой. Если вы решите обработать исключение, вам следует включить блок catch, который вызывает метод Environment.FailFast для завершения вашего приложения и добавления записи в журнал системных событий, как это делается в следующем примере.

Некоторые из условий, при которых выдается исключение, и действия, которые вы можете предпринять для его устранения, включают следующее:

Вы вызываете метод StringBuilder.Insert.

Вы пытаетесь увеличить длину объекта StringBuilder сверх размера, указанного его свойством StringBuilder.MaxCapacity. В следующем примере показано исключение OutOfMemoryException, возникающее при вызове метода StringBuilder.Insert(Int32, String, Int32), когда в примере делается попытка вставить строку, из-за которой свойство Length объекта превысит максимальную емкость.

Для устранения ошибки можно выполнить одно из следующих действий:

Замените вызов конструктора StringBuilder.StringBuilder(Int32, Int32) вызовом любой другой перегрузки конструктора StringBuilder. Максимальная емкость вашего объекта StringBuilder будет установлена ​​на значение по умолчанию, которое равно Int32.MaxValue.

Вызовите конструктор StringBuilder.StringBuilder(Int32, Int32) со значением maxCapacity, достаточно большим для размещения любых расширений объекта StringBuilder.

Ваше приложение работает как 32-разрядный процесс.

32-разрядные процессы могут выделять максимум 2 ГБ виртуальной памяти пользовательского режима в 32-разрядных системах и 4 ГБ виртуальной памяти пользовательского режима в 64-разрядных системах. Это может затруднить для общеязыковой среды выполнения выделение достаточного количества непрерывной памяти, когда требуется большое выделение. Напротив, 64-разрядные процессы могут выделять до 8 ТБ виртуальной памяти. Чтобы устранить это исключение, перекомпилируйте приложение для 64-разрядной платформы. Информацию о настройке конкретных платформ в Visual Studio см. в разделе Практическое руководство. Настройка проектов для целевых платформ.

Из вашего приложения происходит утечка неуправляемых ресурсов

Хотя сборщик мусора может освобождать память, выделенную для управляемых типов, он не управляет памятью, выделенной для неуправляемых ресурсов, таких как дескрипторы операционной системы (включая дескрипторы файлов, отображаемые в память файлы, каналы, ключи реестра и дескрипторы ожидания). ) и блоки памяти, выделяемые непосредственно вызовами Windows API или вызовами функций выделения памяти, таких как malloc . Типы, потребляющие неуправляемые ресурсы, реализуют интерфейс IDisposable.

Если вы используете тип, использующий неуправляемые ресурсы, обязательно вызовите его метод IDisposable.Dispose после завершения его использования. (Некоторые типы также реализуют метод Close, функция которого идентична методу Dispose.) Дополнительные сведения см. в разделе Использование объектов, реализующих IDisposable.

Если вы создали тип, использующий неуправляемые ресурсы, убедитесь, что вы реализовали шаблон Dispose и, при необходимости, предоставили финализатор. Дополнительные сведения см. в разделах Реализация метода Dispose и Object.Finalize.

Вы пытаетесь создать большой массив в 64-разрядном процессе

Вы работаете с очень большими наборами данных (такими как массивы, коллекции или наборы данных базы данных) в памяти.

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

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

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

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

Если вы используете большие массивы или другие объекты коллекций, размер которых приводит к исключению OutOfMemoryException, вам следует изменить приложение, чтобы оно работало с данными в подмножествах, а не со всеми сразу.

В следующем примере получается массив, состоящий из 200 миллионов значений с плавающей запятой, а затем вычисляется их среднее значение. Выходные данные примера показывают, что, поскольку в примере весь массив сохраняется в памяти до вычисления среднего значения, создается исключение OutOfMemoryException.

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

Вы постоянно объединяете большие строки.

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

При объединении больших строк или выполнении большого количества операций объединения следует использовать класс StringBuilder вместо класса String. Закончив работу со строкой, преобразуйте экземпляр StringBuilder в строку, вызвав метод StringBuilder.ToString.

Вы закрепляете большое количество объектов в памяти.

Оцените, действительно ли нужно закрепить каждый объект,

Убедитесь, что каждый объект откреплен как можно скорее.

Убедитесь, что каждый вызов метода GCHandle.Alloc(Object, GCHandleType) для закрепления памяти имеет соответствующий вызов метода GCHandle.Free для открепления этой памяти.

Следующие промежуточные инструкции Microsoft (MSIL) вызывают исключение OutOfMemoryException:

Конструкторы

Инициализирует новый экземпляр класса OutOfMemoryException.

Инициализирует новый экземпляр класса OutOfMemoryException с сериализованными данными.

Инициализирует новый экземпляр класса OutOfMemoryException с указанным сообщением об ошибке.

Инициализирует новый экземпляр класса OutOfMemoryException с указанным сообщением об ошибке и ссылкой на внутреннее исключение, которое является причиной этого исключения.

Свойства

Получает набор пар "ключ-значение", которые предоставляют дополнительную определяемую пользователем информацию об исключении.

Получает или задает ссылку на файл справки, связанный с этим исключением.

Получает или задает HRESULT, закодированное числовое значение, которое назначается определенному исключению.

Получает экземпляр Exception, вызвавший текущее исключение.

Получает сообщение, описывающее текущее исключение.

Получает или задает имя приложения или объекта, вызвавшего ошибку.

Получает строковое представление ближайших кадров в стеке вызовов.

Получает метод, выдающий текущее исключение.

Методы

Определяет, равен ли указанный объект текущему объекту.

При переопределении в производном классе возвращает исключение, являющееся основной причиной одного или нескольких последующих исключений.

Служит хеш-функцией по умолчанию.

При переопределении в производном классе устанавливает SerializationInfo с информацией об исключении.

Получает тип среды выполнения текущего экземпляра.

Создает поверхностную копию текущего объекта.

Создает и возвращает строковое представление текущего исключения.

События

Происходит при сериализации исключения для создания объекта состояния исключения, содержащего сериализованные данные об исключении.

Отладка System.OutOfMemoryException с помощью инструментов .NET

  1. Попытка расширить объект StringBuilder за пределы длины, определенной его свойством StringBuilder.MaxCapacity. К ошибке этого типа обычно прилагается следующее сообщение: «Недостаточно памяти для продолжения выполнения программы».
  2. Среда CLR не может выделить достаточно непрерывной памяти.

Почему? Итак, мы определяем новый StringBuilder с максимальной вместимостью в один символ, а затем пытаемся вставить два символа.

Не говоря уже об этом, давайте поговорим о том, почему вы, вероятно, столкнулись с исключением: CLR не может выделить память, которую запрашивает ваша программа. Чтобы перевести это во что-то понятное вашей маме, ваше приложение использует больше ресурсов, чем доступно.

Ваши пользователи хотели бы меньше ошибок?

Дальше в этом посте я буду использовать и модифицировать пример программы, добавляя строки в список:

Текущая память использование в Perfmon

Сборка мусора во всей красе. Здесь я удалил вызов list.Clear() :

Выделение памяти в Perfmon

Программа продолжает выделять память, пока не возникнет исключение System.OutOfMemoryException.

В этом примере показано, как можно использовать Perfmon для отслеживания состояния приложения. Как и повара на телевидении, я сжульничал и придумал пример для этого поста. В вашем случае вы, вероятно, понятия не имеете, что вызывает интенсивное использование памяти. Профилировщики памяти спешат на помощь!

Распределение памяти в Visual Studio

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

Разница использования памяти в Visual Studio

Глядя на столбец Текущие экземпляры > Новый, становится ясно, что кто-то создает много строк.

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

Читайте также: