Подтвердить что ты не робот

Как обращаться с realloc при сбое из-за памяти?

Вопрос говорит все, но вот пример:

typedef struct mutable_t{
    int count, max;
    void **data;
} mutable_t;


void pushMutable(mutable_t *m, void *object)
{
    if(m->count == m->max){
        m->max *= 2;
        m->data = realloc(m->data, m->max * sizeof(void*));
    }
    // how to handle oom??
    m->data[m->count++] = object;
}

Как я могу обрабатывать исчерпание памяти, а не NULL из всех моих данных?

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

4b9b3361

Ответ 1

Стандартная методика состоит в том, чтобы ввести новую переменную для сохранения возврата из realloc. Затем вы только перезаписываете свою входную переменную, если она преуспевает:

tmp = realloc(orig, newsize);
if (tmp == NULL)
{
    // could not realloc, but orig still valid
}
else
{
    orig = tmp;
}

Ответ 2

Стратегия о том, что делать, когда realloc() терпит неудачу, зависит от вашего приложения. Вопрос слишком общий, чтобы отвечать на все возможные случаи.

Некоторые другие примечания:

Никогда:

a = realloc(a, size);

Если realloc() не работает, вы теряете исходный указатель, а realloc() не free() исходной памяти, поэтому вы получите утечку памяти. Вместо этого выполните:

tmp = realloc(a, size);
if (tmp)
    a = tmp;
else
    /* handle error */

Вторая точка, которую я хочу сделать, является незначительной и может быть не такой критической, но хорошо знать об этом в любом случае: увеличение объема памяти, выделяемого фактором f, является хорошим. Скажем, сначала вы malloc() n байтов. Тогда вам нужно больше памяти, поэтому вы realloc() с размером n & times; f. Затем вам нужно больше памяти, поэтому вам понадобятся n & times; f 2 байты. Если вы хотите, чтобы realloc() использовал пробел из предыдущих двух блоков памяти, вы должны убедиться, что n & times; f 2 & le; n + n & times; f. Решая это уравнение, получаем f & le; (sqrt (5) +1)/2 = 1,618 (Золотое соотношение). Я использую коэффициент 1.5 большую часть времени.

Ответ 3

Это немного горячая тема темы, поскольку в этой теме есть по существу 2 школы мысли

  • Обнаружение OOM и функция возвращает код ошибки.
  • Обнаружение OOM и быстрый сбой вашего процесса

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

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

Ответ 4

Первое правило, которое вы следуете при работе с realloc, не должно присваивать возвращаемое значение realloc тому же указателю, который вы ему передали. Это

m->data = realloc(m->data, m->max * sizeof(void*)); 

Плохо. Если realloc терпит неудачу, он возвращает нулевой указатель, но он не освобождает старую память. В приведенном выше коде будет нулевое значение m->data, в то время как старый блок памяти, ранее указавший на m->data, скорее всего, станет утечкой памяти (если у вас нет других ссылок на него).

Возвращаемое значение realloc должно быть сначала сохранено в отдельном указателе

void **new_data;
...
new_data = realloc(m->data, m->max * sizeof(void*)); 

Затем вы можете проверить успех/неудачу и изменить значение m->data в случае успеха

if (new_data != NULL)
  m->data = new_data;
else
  /* whatever */;

Ответ 5

Это твоя проблема! Вот некоторые критерии:

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

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

  • Неправильно ли ваша программа продолжать хромать без этих данных и недостаточно памяти? Если это так, вы должны завершиться с сообщением об ошибке. Гораздо лучше убить вашу программу, чем слепо продолжить обработку некорректных данных.

Ответ 6

  • Узнайте, как среда приложения обрабатывает OOM. Многие просто не будут обрабатывать OOM. В большинстве случаев структура не будет работать должным образом в условиях без свободной ОЗУ, если она не будет четко и однозначно говорить где-то, что она будет делать. Если фреймворк не будет обрабатывать OOM и многопоточен (многие из них в настоящее время), OOM в конце концов станет окончанием шоу для процесса. Даже если он не многопоточен, он все равно может быть близок к краху. Если вы выходите из процесса или фреймворк может быть спорным; предсказуемый немедленный выход может быть немного лучше, чем сбой в некоторой полуслучайной точке в ближайшем будущем.

  • Если вы используете отдельный пул вспомогательной памяти специального назначения (т.е. не ваш обычный malloc) для четко определенного набора операций, которые только ограничены в использовании памяти OOM (т.е. текущая операция откатывается или автоматически прерывается в OOM для пула вспомогательной памяти, а не для всего процесса или основного пула памяти), и этот подпункт также не используется каркасом приложения, или если ваша структура и ЦЕЛЬ остальных приложение предназначено для поддержания значимого состояния и продолжения работы в условиях без свободной RAM (редко, но не неслыханно в режиме ядра и некоторых типах системного программирования), вы можете быть правы, чтобы вернуть код ошибки, а не сбой процесса.

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

  • Даже если вы проверяете, сколько памяти сообщается как доступно, часто другой код может выделять или освобождать память, как вы, изменяя базу для проверки вашей памяти и, возможно, приводя к OOM. Таким образом, проверка доступности бесплатной ОЗУ до того, как вы выделите, часто не является надежным решением проблемы обеспечения работоспособности вашего приложения в пределах доступных пределов оперативной памяти и поддержания целостности данных достаточно времени, чтобы удовлетворить пользователей.

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

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

Что касается realloc:

Проверить возвращаемое значение из realloc - поместить его во временную переменную. Только если это NULL, если запрашиваемый новый размер был > 0. В других случаях поместите его в свою временную переменную:

например,

    void* temp = realloc(m->data, m->max * sizeof(void*));
    if (m->max!=0&&temp==NULL) { /* crash or return error */ }
    m->data =(void**)temp;

ИЗМЕНИТЬ

Изменено "большинство случаев" во "множество случаев" в (1).

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

Ответ 7

Там также есть еще одна тонкая ошибка, которая может возникнуть из realloc. Утечка памяти, поступающая из возвращаемого указателя NULL, довольно хорошо известна (но довольно редко натыкается). Иногда в моей программе произошел сбой, вызванный вызовом realloc. У меня была динамическая структура, которая автоматически меняла свой размер с помощью realloc, напоминающего этот:

m->data = realloc(m->data, m->max * sizeof(void*)); 

Я сделал ошибку, чтобы не проверять m- > max == 0, что освободило область памяти. И сделанный из моего указателя m- > data устаревшим.

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

Ответ 8

Я столкнулся с проблемой. Конфигурация - OS: win7 (64); IDE: vs2013; Debug (Win32).
Когда мой realloc возвратил null из-за памяти в. У меня есть два решения:

1.Измените свойство проекта, чтобы разрешить БОЛЬШИЕ АДРЕСА.
2. Измените платформу решений от Win32 до x64.