Другие возможные причины выброса std::bad_alloc

#c #linux #exception #g #heap-memory

#c #linux #исключение #g #куча-память

Вопрос:

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

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

Но мой вопрос в том, есть ли какие-либо другие причины, по которым std::bad_alloc будет выброшен, кроме нехватки памяти или повреждения кучи? Я использую GNU g в Linux.

Комментарии:

1. Насколько я знаю или видел, нет.

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

3. @Крис Додд: или до неопределенного поведения, я полагаю!

Ответ №1:

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

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

Сильная фрагментация может вызвать проблему, особенно для платформ с плохими реализациями malloc . Это редко, но случается.

Одна вещь, которую я бы сделал немедленно — перехватил исключение и вызвал функцию, которая сохраняет копию /proc/self/maps . Это даст вам хорошее представление о максимальном использовании памяти процессом. Вы можете судить, соответствует ли это каким-либо ограничениям платформы, политики или аппаратного обеспечения.

Ответ №2:

В Linux текущее ограничение адресного пространства может использоваться для искусственного ограничения объема памяти, который может использовать процесс. Вы можете вручную установить это с помощью setrlimit(RLIMIT_AS, ...) . Это также может быть установлено для всей оболочки при bashrc использовании ulimit -v . Это также может быть установлено для всей системы в /etc/security/limits.conf . Возможно, где-то даже есть запись /proc / sys для этого, я не уверен.

Если достигнут предел адресного пространства, ваш процесс выдаст std::bad_alloc при попытке выделить больше памяти. В 64-разрядной системе это может быть хорошей «защитой», позволяющей убедиться, что плохое приложение или библиотека не исчезнут с доступной памятью и не заставят систему перейти на swap или вообще прекратить работу. Убедитесь, что программа сама не установила это где-нибудь, и убедитесь, что остальная часть среды также не установила это. Вы можете просто вставить некоторый код в середине программы в какое-нибудь место для вызова getrlimit(RLIMIT_AS, ...) , чтобы быть уверенным, что он где-нибудь не застрял.

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

В любом случае, лучшие способы отследить это — с помощью GDB. Если ваше приложение вообще не использует исключения (и, следовательно, вообще не имеет инструкций catch), вы можете включить core files ( ulimit -c unlimited ). При следующем сбое программы ОС сгенерирует основной файл и, загрузив его в GDB, немедленно предоставит вам обратную трассировку, показывающую, где произошел сбой программы.

Если у вас есть несколько (но не так много) мест, где используется функция try и она улавливает эти неправильные выделения, а не просто комментирует их во время отладки этой проблемы, вы можете запустить приложение в GDB и использовать catch throw команду для прерывания GDB при каждом возникновении исключения. Чтобы любой из этих способов работал, никогда не компилируйте с -fomit-frame-pointer и всегда компилируйте (даже при использовании -O3 ) с -ggdb .