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

Может ли оператор С++ `new` когда-либо вызывать исключение в реальной жизни?

Может ли оператор new исключить исключение в реальной жизни?

И если да, есть ли у меня какие-либо опции для обработки такого исключения, кроме убийства моего приложения?

Update:

Какие-либо реальные, new -ные тяжелые приложения проверяют на отказ и восстанавливают, когда нет памяти?


См. также:

4b9b3361

Ответ 1

Новый оператор и новый оператор [] должны бросать std::bad_alloc, но это не всегда так, поскольку поведение иногда может быть переопределено.

Можно использовать std::set_new_handler, и вдруг может произойти что-то совсем другое, чем бросать std::bad_alloc. Хотя стандарт требует, чтобы пользователь либо делал память доступной, прервал, либо выбрал std::bad_alloc. Но, конечно, это может быть не так.

Отказ от ответственности: я не предлагаю этого.

Ответ 2

Да, new может и будет выдаваться, если распределение не выполняется. Это может произойти, если у вас закончилась нехватка памяти или вы попытаетесь выделить слишком большой блок памяти.

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

Ответ 3

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

Если вы запускаете свою программу на машине с меньшей физической памятью, чем максимальная виртуальная память (2 ГБ на стандартной Windows), вы обнаружите, что как только вы выделили объем памяти, приблизительно равный доступной физической памяти, выделения будут успешными, но вызовет пейджинг на диск. Это заглушит вашу программу, и на самом деле вы не сможете дойти до истощения виртуальной памяти. Таким образом, вы не можете получить исключение.

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

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

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

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

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

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

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

Ответ 4

В Unix-системах обычно запускаются длительные процессы с ограничениями памяти (с использованием ulimit), чтобы он не ест всю системную память. Если ваша программа достигает этого предела, вы получите std::bad_alloc.


Обновление для редактирования OP: наиболее типичный случай программ, восстанавливаемых из состояния нехватки памяти, - в сборках с мусором, который затем выполняет GC и продолжается. Хотя, этот GC по требованию действительно предназначен только для последних усилий; обычно, хорошие программы периодически пытаются GC, чтобы уменьшить стресс на коллекторе.

Менее обычным для программ, не относящихся к GC, для восстановления из-за проблем с памятью, но для серверов, обращенных к Интернету, один из способов восстановления - просто отказаться от запроса, который заставляет память работать с "временным", ошибка. (Стратегия "Первый в первом порядке" ).

Ответ 5

Вам не нужно обрабатывать исключение в каждом отдельном new:) Исключения могут распространяться. Создайте свой код так, чтобы в каждом "модуле", где эта ошибка обрабатывалась, есть определенные точки.

Ответ 6

Это зависит от компилятора/времени выполнения и от используемого вами operator new (например, некоторые версии Visual Studio не будут выбрасываться из коробки, но скорее вернет указатель NULL a la malloc.)

Вы всегда можете исключить catch a std::bad_alloc или явно использовать nothrow new для возврата NULL вместо метания. (Также см. fooobar.com/questions/157905/..., вращающихся вокруг объекта.)

Обратите внимание, что operator new, например malloc, будет выходить из строя, когда у вас закончилась нехватка памяти, из адресного пространства (например, 2-3 ГБ в 32-битном режиме в зависимости от ОС), из квоты (ulimit уже упоминалось) или из смежного адресного пространства (например, фрагментированная куча).

Ответ 7

osgx сказал:

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

Я ответил на это ранее в моем ответе на этот вопрос, который приведен ниже:

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

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

Ответ 8

Да, new может бросить std::bad_alloc (подкласс std::exception), который вы можете поймать.

Если вы абсолютно хотите избежать этого исключения и вместо этого готовы проверить результат new для нулевого указателя, вы можете добавить аргумент nothrow:

T* p = new (nothrow) T(...);
if (p == 0)
{
    // Do something about the bad allocation!
}
else
{
    // Here you may use p.
}

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Обратите внимание, что в Windows очень большие новые /mallocs будут просто выделяться из виртуальной памяти. На практике ваша машина выйдет из строя, прежде чем вы увидите это исключение.

char *pCrashMyMachine = new char[TWO_GIGABYTES];

Попробуйте, если вы посмеете!

Ответ 12

Я использую Mac OS X, и я никогда не видел malloc return NULL (что подразумевало бы исключение из new в С++). Машина боится вниз, делает все возможное, чтобы выделить сокращающуюся память для процессов и, наконец, отправляет SIGSTOP и предлагает пользователю убивать процессы, а не иметь дело с отказом в распределении.

Однако, это только одна платформа. В ПОСТОЯННОстях есть платформы, в которых распределитель по умолчанию бросает. И, как говорит Крис, ulimit может ввести искусственное ограничение, чтобы исключение было ожидаемым поведением.

Кроме того, существуют распределители помимо значения по умолчанию //t 20. Если класс переопределяет operator new, вы используете пользовательские аргументы в new(…) или передаете объект-распределитель в контейнер, он, вероятно, определяет свои собственные условия для сброса bad_alloc.

Ответ 13

новый оператор будет генерировать исключение std:: bad_alloc, если в пуле недостаточно доступной памяти для выполнения запроса времени выполнения.

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

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

Ответ 14

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

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

Ответ 15

Да, новый может и будет бросать.

Поскольку вы спрашиваете о "реальных" программах: я работал над различными коммерческими программными приложениями с термоусадочной пленкой более 20 лет. "Реальные" программы с миллионами пользователей. Сегодня вы можете пойти и выкупить полку. Да, новый может бросить.

Существуют различные способы справиться с этим.

Сначала напишите свой собственный new_handler (это вызывается до того, как новый отбрасывает и бросает - см. функцию set_new_handler()). Когда ваш new_handler вызывается, посмотрите, можете ли вы освободить некоторые вещи, которые вам действительно не нужны. Также предупреждайте пользователя о том, что они мало работают в памяти. (да, может быть сложно предупредить пользователя о чем угодно, если вы действительно низки).

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

Etc. Это всего лишь обзор, очевидно, что для него есть еще больше.

Работа с низкой памятью непростая.

Ответ 16

Функция нового обработчика - это функция, называемая функциями распределения, всякий раз, когда возникает новая попытка выделения памяти. У нас может быть собственный журнал или какое-то специальное действие, например, g для большей памяти и т.д. Его цель - одно из трех: 1) сделать доступной память 2) завершение программы (например, вызов std:: terminate) 3) исключить тип типа std:: bad_alloc или полученный из std:: bad_alloc. Реализация по умолчанию бросает std:: bad_alloc. Пользователь может иметь свой собственный новый обработчик, который может предлагать поведение, отличное от стандартного. Это должно использоваться только тогда, когда вам действительно нужно. См. Пример для более подробного пояснения и поведения по умолчанию,

#include <iostream>
#include <new>

void handler()
{
    std::cout << "Memory allocation failed, terminating\n";
    std::set_new_handler(nullptr);
}

int main()
{
    std::set_new_handler(handler);
    try {
        while (true) {
            new int[100000000ul];
        }
    } catch (const std::bad_alloc& e) {
        std::cout << e.what() << '\n';
    }
}

Ответ 17

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

Ответ 18

Оператор

new выкинет исключение std::bad_alloc, когда закончится память (точнее, виртуальная память).

Если new выдает исключение, это серьезная ошибка:

  • Больше, чем доступная виртуальная машина получает выделение (в конечном итоге она терпит неудачу). Вы можете попытаться уменьшить объем памяти, чем выйти из программы, поймав исключение std::bad_alloc.