Выдает alloca() память, если выброшено исключение?
Я поддерживаю устаревшее приложение C++, которое, похоже, имеет медленную утечку памяти. Мне удалось "исправить" утечку памяти, убедившись, что текущая конфигурация больше не выдает никаких исключений, и я также могу вызвать утечку и масштабировать ее, настроив ее так, чтобы она приводила ко многим исключениям.
Вся выделенная память выполняется с помощью alloca(), а не malloc(). Я объяснил это тем, что alloca() работает как сборщик мусора java и автоматически освобождает память при выходе из контекста.
Из-за того, что утечка так явно связана с создаваемыми исключениями, у меня есть теория, что alloca() не освобождает память, когда генерируются исключения.
Это вообще правдоподобно? Мне кажется, что это серьезный дефект в alloca(), если он истинен, но когда я запускаю Google alloca(), это, как правило, является проблемой.
Я был бы признателен за понимание любых экспертов.
3 ответа
Вполне вероятно, что проблема связана с уровнем косвенности.
Буквальный вопрос "делает alloca
вернуть память, если выброшено исключение?". И ответ на этот вопрос; он возвращает только память, которая была выделена напрямую. Деструкторы не запускаются, и любой владеющий указатель внутри alloca
Выделенная память просочилась.
В C++ вы не должны использовать процедуры управления памятью C. В современном C++ интеллектуальные указатели предоставляют вам детализированную сборку мусора.
Хотя пространство выделено через alloca()
вероятно, освобождается от исключений (потому что это обычно делается путем увеличения размера текущего стекового кадра). Это не является частью стандарта, и поэтому я не думаю, что вы можете дать какие-либо гарантии.
НО Это также означает, что любые подходящие деструкторы на объекте никогда не будут вызываться. Это означает, что если объект выполняет свое собственное управление памятью, он не будет очищен (потому что деструктор не запускается для его очистки).
Хоть alloca()
вероятно очень быстро Я думаю, что дополнительная нагрузка на управление памятью (в общем случае) не стоит; хотя, если у вас есть особая потребность в дополнительной скорости, это может стоить того.
Код выглядит так:
void func()
{
MyType* x = (MyType*)alloca(sizeof(MyType));
passXtoCFunctionThatDoesNotTakeOwnership(x);
}
Должно быть написано так:
void func()
{
std::unique_ptr<MyType> x = std::make_unique<MyType>();
passXtoCFunctionThatDoesNotTakeOwnership(x.get());
}
Если вы используете его для хранения массива объектов.
void func()
{
MyType* x = (MyType*)alloca(sizeof(MyType) * arraySize);
// STUFF
x[0].stuff();
}
Тогда лучше использовать std::vector
void func()
{
std::vector<MyType> x;
x.reserve(arraySize); // or resize() if that is appropriate
// STUFF
x[0].stuff();
}
Если вы используете его для простых объектов. Тогда вам, вероятно, следует просто объявить автоматические переменные:
void func()
{
MyType* x = (MyType*)alloca(sizeof(MyType));
x->myData = 5;
}
Вы должны просто объявить переменную:
void func()
{
MyType x;
x.myData = 5;
}
Из Linux man
....
Поскольку пространство, выделенное alloca(), выделяется внутри фрейма стека, это пространство автоматически освобождается, если возврат функции перепрыгивается при вызове longjmp(3) или siglongjmp(3).
....
Встроенный код часто состоит из одной инструкции, регулирующей указатель стека, и не проверяет переполнение стека. Таким образом, нет возврата NULL.
.....
Ошибки Нет индикации ошибок, если кадр стека не может быть расширен. (Однако после неудачного выделения программа, вероятно, получит сигнал SIGSEGV, если попытается получить доступ к нераспределенному пространству.) Во многих системах alloca() не может использоваться в списке аргументов вызова функции, поскольку пространство стека зарезервированный alloca() появится в стеке в середине пространства для аргументов функции.
Таким образом, исключение не может вызвать утечку памяти для памяти, выделенной alloca в кадре стека. Однако это, как и любое исключение, может привести к утечкам памяти в куче, поскольку деструкторы и методы освобождения памяти, размещенные после alloca, будут пропущены.