MemoryFailPoint всегда генерирует исключение InsufficientMemoryException, даже если память доступна

Я написал следующий код для проверки достаточной памяти,

while (true)
{
    try
    {
        // Check for available memory.
        memFailPoint = new MemoryFailPoint(250);

        break;
    }
    catch (InsufficientMemoryException ex)
    {
        if (memFailPoint != null)
        {
          memFailPoint.Dispose();
        }

        Thread.Sleep(waitSecond * 1000);
    }
}

Я запускаю вышеуказанное в консольном приложении на 64-разрядной машине Windows 7.

Для этого метода используется 4 вызова каждые 10 секунд.

Изначально он отлично работает, но через 2-3 часа всегда вызывается InsufficientMemoryException. Я проверил доступную память и отобразил более 1 ГБ.

Я много пробовал, но не смог найти, почему это происходит.

Ниже показана трассировка стека:

at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes)
at SocketListner.AcceptConnection(IAsyncResult res) in H:\Projects\SocketListner.cs:line 308

Внутреннее исключение отсутствует.

Ответ 1

Вы можете положиться на этот метод, работающий корректно, это исключение очень, которое может совершить поездку в 32-битном процессе, когда вы запрашиваете 250 мегабайт. Это становится трудно получить, когда программа работает некоторое время.

Программа никогда не сбой с OOM, потому что вы потребляли все доступное адресное пространство виртуальной памяти. Он выходит из строя, потому что в адресном пространстве нет дыры, достаточно большой, чтобы соответствовать распределению. Ваш код требует отверстия достаточно большого, чтобы выделить 250 мегабайт в одном gulp. Когда вы не получите исключение, вы можете быть уверены, что это распределение не будет выполнено.

Но 250 мегабайт довольно много, это действительно большой массив. И, скорее всего, не удастся из-за проблемы, называемой "фрагментация адресного пространства". Другими словами, программа, как правило, начинается с нескольких очень больших отверстий, самая большая около 600 мегабайт. Доступны отверстия между распределениями, предназначенными для хранения кода и данных, которые используются средой выполнения .NET и неуправляемыми библиотеками Windows. Поскольку программа выделяет больше памяти, эти отверстия становятся меньше. Вероятно, он выпустит некоторую память, но это не воспроизводит большую дыру. Обычно вы получаете две дыры, примерно половину от размера оригинала, с расположением где-то посередине, которое разрезает исходное большое отверстие на два.

Это называется фрагментацией, 32-разрядный процесс, который выделяет и освобождает много памяти, заканчивает фрагментацию адресного пространства виртуальной памяти, поэтому самое большое отверстие, которое все еще доступно через некоторое время, становится меньше, около 90 мегабайт довольно типично. Спрос на 250 мегабайт почти гарантированно провалится. Вам нужно будет опуститься ниже.

Вы, без сомнения, ожидали, что он будет работать по-другому, гарантируя, что сумма ассигнований, суммирующая до 250 мегабайт, будет работать. Однако это не так, как работает MemoryFailPoint, оно проверяет только максимально возможное распределение. Излишне говорить, что, возможно, это делает его менее полезным. В противном случае я сочувствую программистам на платформе .NET, чтобы заставить его работать так, как нам бы хотелось, и он дорог, и фактически не может гарантировать гарантии, поскольку размер распределения имеет наибольшее значение.

Виртуальная память - это богатый ресурс, который невероятно дешев. Но приблизиться к потреблению - это очень хлопотно. Как только вы потребляете гигабайт, тогда OOM, поражающий наугад, начинает становиться вероятным. Не забывайте про легкое исправление этой проблемы, вы работаете в 64-битной операционной системе. Таким образом, простое изменение целевой платформы EXE на AnyCPU дает вам gobs и gobs виртуального адресного пространства. Зависит от выпуска ОС, но возможен терабайт. Это все еще фрагменты, но вам просто все равно, дыры огромны.

Последнее, но не менее важное, видимое в комментариях, эта проблема имеет ничего для работы с ОЗУ. Виртуальная память совершенно не связана с тем, сколько у вас RAM. Задача операционной системы состоит в том, чтобы сопоставлять адреса виртуальной памяти с физическими адресами в ОЗУ, она делает это динамически. Доступ к ячейке памяти может привести к сбою в ошибке страницы, ОС будет выделять ОЗУ для этой страницы. И происходит обратное: ОС будет отформатировать RAM для страницы, когда она понадобится в другом месте. Вы никогда не можете выбежать из ОЗУ, машина замедлит сканирование до того, как это произойдет. Утилита VMMap SysInternals приятно видеть, как выглядит ваше виртуальное адресное пространство вашей программы, хотя вы склонны утонуть в информации для большого процесса.

Ответ 2

Рассмотрите возможность использования метода GC.GetTotalMemory для определения объема доступной памяти до и после вызова:

memFailPoint = new MemoryFailPoint(250);

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

Пример этой ссылки должен дать вам решение: класс MemoryFailPoint

Вы также можете проверить эту статью в блоге msdn: Не хватает памяти? Простые способы увеличения объема памяти, доступной для вашей программы

Ответ 3

MemoryFailPoint проверяет наличие последовательной доступной памяти, как описано здесь: http://msdn.microsoft.com/fr-fr/library/system.runtime.memoryfailpoint.aspx

Вы можете потреблять очень мало памяти, но сильно ее фрагментировали, а затем теперь не можете выделить последовательный блок памяти необходимого размера. Это очень типично для этой проблемы, которая возникает через несколько часов. Чтобы этого избежать, используйте пул объектов для объекта, который вы создаете для создания экземпляра, это сделает пространство памяти более жестким.