Как обрабатывать 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;
}

Как я могу справиться с нехваткой памяти и не обнулить все мои данные?

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

8 ответов


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

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

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

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

никогда не делать:

a = realloc(a, size);

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

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

второй момент, который я хочу сделать, является незначительным и не может быть, что критическая, но это хорошо знать об этом в любом случае: увеличение памяти, которая будет выделена фактором f - это хорошо. Допустим, вы malloc() n байт первый. Тогда вам нужно больше памяти, так что вы realloc() в размере н×ф. Тогда вам нужно больше памяти, так что вам нужно н×ф2 байт. Если вы хотите realloc() чтобы использовать пространство из предыдущих двух блоков памяти, вы хотите убедиться, что н×ф2 ≤ n + N×f. Решение этой уравнение, получаем f≤ (sqrt(5)+1)/2 = 1.618 (the золотой пропорции). Я использую коэффициент 1.5 большую часть времени.


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

  1. обнаружьте OOM, и функция вернет код ошибки.
  2. обнаружение OOM и сбой вашего процесса как можно быстрее

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

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


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

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

- это плохо. Если realloc не удается, он возвращает нулевой указатель, но он не освобождает старую память. Приведенный выше код будет null ваш 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 */;

это полностью ваша проблема! Вот некоторые критерии:

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

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

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


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

  2. Если вы используете отдельных специальных суб-пула памяти (т. е. не ваш обычный malloc) для четко определенного набора операций, которые только ограничены в использовании памяти по ООМ (т. е. текущая операция откатывается назад или прервана чисто по ООМ для суб-пула памяти, а не весь процесс или основной пул памяти), и этот под-пул также не используется платформой приложений, или если ваша платформа и вся остальная часть приложения предназначена для поддержания значимого состояния и продолжения работы в условиях без свободной оперативной памяти (редко, но не неслыханно в режиме ядра и некоторых типах системного программирования), вы можете быть правы, чтобы вернуть код ошибки, а не сбой процесса.

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

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

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

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

в отношении 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).

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


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

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

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

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


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

1.Измените свойство проекта, чтобы включить большие адреса.
2.Измените платформу решения с Win32 на x64.