Обнаружение, когда заканчивается нехватка памяти (получение объема «свободной физической памяти») - PullRequest
6 голосов
/ 18 декабря 2010

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

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

Теперь проблема в том, что я не могу найти способ фактически получить это значение программным путем;в XP он даже нигде не отображается (только в диспетчере задач Vista / 7).

alt text

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

Есть идеи?

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

Ответы [ 6 ]

9 голосов
/ 18 декабря 2010

Соотношение - это не причинно-следственная связь. Вы можете «исчерпать память», даже если нагрузка на физическую память все еще свободна. Физическая память почти наверняка не имеет значения; вероятно, у вас заканчивается адресное пространство .

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

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

Не делай этого. Вытащите блок разумного размера, выгрузите его на диск, а затем при необходимости обработайте файл на диске.

7 голосов
/ 29 декабря 2010

Я опаздываю на вечеринку, но вы рассматривали возможность использования класса System.Runtime.MemoryFailPoint ?Он делает кучу вещей, чтобы гарантировать, что запрошенное выделение будет выполнено успешно, и выдает исключение InsufficientMemoryException в случае сбоя;Вы можете поймать это и остановить ваш перевод.Вероятно, вы можете предсказать средний размер входящих кадров и попытаться выделить 3 или 4 из них, а затем прекратить получение при возникновении сбоя.Может быть, что-то вроде этого?

const int AverageFrameSize = 10 * 1024 * 1024 // 10MB

void Image_OnAcquired(...)
{
    try
    {
        var memoryCheck = new MemoryFailPoint(AverageFrameSize * 3);
    }
    catch (InsufficientMemoryException ex)
    {
        _camera.StopAcquisition();
        StartWaitingForFreeMemory();
        return;
    }

    // if you get here, there's enough memory for at least a few
    // more frames
}

Я сомневаюсь, что это будет на 100% надежно, но это только начало.Он определенно более надежен, чем счетчик производительности, по причинам, которые объясняются в других ответах.

2 голосов
/ 18 декабря 2010

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

Linky: http://www.codinghorror.com/blog/2006/09/why-does-vista-use-all-my-memory.html

Кроме того, если вы 'Выполняя 32-битный процесс C #, вы в любом случае ограничены 2 ГБ памяти на процесс (на самом деле больше, чем 1,5 ГБ на практике, прежде чем ситуация станет нестабильной), поэтому даже если ваш ящик показывает, что у вас достаточно свободной памяти, вы все равно получитеисключение памяти, когда ваш процесс достигает предела в 2 ГБ.

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

1 голос
/ 06 ноября 2015

Хотел добавить свой собственный ответ, потому что в противном случае хороший ответ от OwenP имеет две важные ошибки при использовании System.Runtime.MemoryFailPoint .

Первую ошибку очень просто исправить: сигнатура конструктора public MemoryFailPoint(int sizeInMegabytes), поэтому аргумент AverageFrameSize должен быть в мегабайтах, а не в байтах.Также обратите внимание на следующее о размере:

MemoryFailPoint работает с гранулярностью 16 МБ.Любые значения, меньшие 16 МБ, обрабатываются как 16 МБ, а другие значения обрабатываются как следующий по величине кратный 16 МБ.

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

Это может быть немного сложнее исправить и может потребовать внесения изменений в дизайн в зависимости от того, как выглядит реальный код OPнапример.

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

Память, "зарезервированная" конструктором, не резервируется при вызове Dispose().Таким образом, поток должен располагать MemoryFailPoint -инстанцией как можно скорее после , ему выделена необходимая память , но не ранее .(Часть «как можно скорее» предпочтительна, но не критична. Задержка утилизации может привести к ненужным сбоям других проверок памяти, но, по крайней мере, вы ошибаетесь на консервативной стороне.)

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

Использование первого подхода (и фиксированный размер буфера) может выглядеть примерно так:

const int FrameSizeInMegabytes = 10; // 10MB (perhaps more is needed?)
const int FrameSizeInBytes = FrameSizeInMegabytes << 20;
// shifting by 20 is the same as multiplying with 1024 * 1024.

bool TryCreateImageBuffer(int numberOfImages, out byte[,] imageBuffer)
{
    // check that it is theoretically possible to allocate the array.
    if (numberOfImages  < 0 || numberOfImages > 0x7FFFFFC7)
        throw new ArgumentOutOfRangeException("numberOfImages",
            "Outside allowed range: 0 <= numberOfImages <= 0x7FFFFFC7");

    // check that we have enough memory to allocate the array.
    MemoryFailPoint memoryReservation = null;
    try
    {
        memoryReservation =
            new MemoryFailPoint(FrameSizeInMegabytes * numberOfImages);
    }
    catch (InsufficientMemoryException ex)
    {
        imageBuffer = null;
        return false;
    }

    // if you get here, there's likely to be enough memory
    // available to create the buffer. Normally we can't be
    // 100% sure because another thread might allocate memory
    // without first reserving it with MemoryFailPoint in
    // which case you have a race condition for the allocate.
    // Because of this the allocation should be done as soon
    // as possible - the longer we wait the higher the risk.
    imageBuffer = new byte[numberOfImages, FrameSizeInBytes];

    //Now that we have allocated the memory we can go ahead and call dispose
    memoryReservation.Dispose();

    return true;
}

0x7FFFFFC7 - это максимальный индексатор, допустимый в любом измерении для массивов однобайтовых типов; его можно найти на странице MSDN о массивах .

Второй подход (где вызывающий объект отвечает за экземпляр MemoryFailPoint) может выглядеть примерно так:

const int AverageFrameSizeInMegabytes = 10; // 10MB

/// <summary>
/// Tries to create a MemoryFailPoint instance for enough megabytes to
/// hold as many images as specified by <paramref name="numberOfImages"/>.
/// </summary>
/// <returns>
/// A MemoryFailPoint instance if the requested amount of memory was
/// available (at the time of this call), otherwise null.
/// </returns>
MemoryFailPoint GetMemoryFailPointFor(int numberOfImages)
{
    MemoryFailPoint memoryReservation = null;
    try
    {
        memoryReservation =
            new MemoryFailPoint(AverageFrameSizeInMegabytes * numberOfImages);
    }
    catch (InsufficientMemoryException ex)
    {
        return null;
    }
    return memoryReservation;
}

Это выглядит намного проще (и более гибко), но теперь решать самому вызывающемуобрабатывать экземпляр MemoryFailPoint и утилизировать его в нужный момент времени.(Добавлена ​​некоторая обязательная документация, так как я не придумал хорошее и описательное имя для метода.)

Важное замечание: Что означает «зарезервировано» в этом контексте

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

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

// Note that multiple threads can still ---- on our free chunk of address space, which can't be easily solved.

Нетрудно догадаться, что такое цензурированное слово.


Вот интересная статья о , как преодолеть ограничение в 2 ГБ для массивов .

Также, если вам нужно выделить очень большие структуры данных, вам нужно знать о <gcAllowVeryLargeObjects>, которые вы можете установить в своем app-config.


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

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

1 голос
/ 27 декабря 2010

Спасибо за все ответы.

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

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

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

0 голосов
/ 18 декабря 2010

Получение исключения OutOfMemoryException означает, что текущее распределение памяти не может быть выполнено. Это не обязательно означает, что системе или даже процессу не хватает памяти. Представьте себе приложение hello world type, которое запускается с выделения 2 ГБ памяти. В 32-битной системе это, скорее всего, вызовет исключение, несмотря на тот факт, что процесс на самом деле не выделил значительную память на данный момент.

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

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