Может C++, 'новый' оператор когда-нибудь выдает исключение в реальной жизни?

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

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

Обновление:

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


См. также:

44
задан Community 23 May 2017 в 12:26
поделиться

16 ответов

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

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

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

24
ответ дан 26 November 2019 в 21:42
поделиться

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

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

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

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


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

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

9
ответ дан 26 November 2019 в 21:42
поделиться

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

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

1
ответ дан 26 November 2019 в 21:42
поделиться

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

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

3
ответ дан 26 November 2019 в 21:42
поделиться

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

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

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

1
ответ дан 26 November 2019 в 21:42
поделиться

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

7
ответ дан 26 November 2019 в 21:42
поделиться

Да, новый может и выкинет.

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

Есть разные способы справиться с этим.

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

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

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

Обработка недостатка памяти - непростая задача.

1
ответ дан 26 November 2019 в 21:42
поделиться

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

Однако это только одна платформа. Совершенно очевидно, что есть платформы, где аллокатор по умолчанию действительно бросает. И, как говорит Крис, ulimit может ввести искусственное ограничение, так что исключение будет ожидаемым поведением.

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

2
ответ дан 26 November 2019 в 21:42
поделиться

Да, 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.
}
4
ответ дан 26 November 2019 в 21:42
поделиться

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

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

4
ответ дан 26 November 2019 в 21:42
поделиться

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

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

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

7
ответ дан 26 November 2019 в 21:42
поделиться

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

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

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

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

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

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

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

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

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

19
ответ дан 26 November 2019 в 21:42
поделиться

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

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

40
ответ дан 26 November 2019 в 21:42
поделиться

osgx сказал:

Проверяет ли какое-либо реальное приложение большое количество новостей и может {{1} } восстанавливать, когда нет памяти?

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

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

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

7
ответ дан 26 November 2019 в 21:42
поделиться

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

char *pCrashMyMachine = new char[TWO_GIGABYTES];

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

3
ответ дан 26 November 2019 в 21:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: