我正在维护一个似乎内存泄漏缓慢的旧版C ++应用程序。通过确保当前配置不再引发任何异常,我设法“修复”了内存泄漏,并且我还可以通过配置泄漏导致许多异常来触发泄漏并对其进行扩展。
所有分配的内存都是使用alloca()而不是malloc()完成的。我为此给出的解释是,因为alloca()的工作方式类似于Java垃圾收集器,并且在退出上下文时自动释放内存。
由于泄漏被清楚地绑定到抛出的异常,所以我有一个理论,当抛出异常时,alloca()无法释放内存。
这完全合理吗?如果为true,这将使我成为alloca()的主要缺陷,但是当我使用Google alloca()时,这似乎通常是个问题。
对于任何专家的见解,我将不胜感激。
答案 0 :(得分:3)
该问题很可能是由于间接级别引起的。
字面问题是“如果引发异常,position='dodge'
是否返回内存?”。答案是:它仅返回仅已分配的内存。不运行任何析构函数,并且alloca
分配的内存中的所有拥有者的指针都会泄漏。
答案 1 :(得分:2)
在C ++中,您不应使用C内存管理例程。在现代C ++中,智能指针为您提供了细粒度的确定性垃圾回收。
尽管通过alloca()
分配的空间可能已分配了异常(因为通常是通过增加当前堆栈帧的大小来完成的)。这不是标准的一部分,因此我认为您无法做出任何保证。
BUT 这也意味着永远不会调用该对象上的任何适当的析构函数。这意味着如果对象执行自己的内存管理,则不会清除该对象(因为没有运行析构函数来对其进行清理)。
尽管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;
}
答案 2 :(得分:0)
从Linux man
....
因为alloca()分配的空间是在堆栈内分配的 框架,该空间会自动释放如果函数返回为 通过调用longjmp(3)或siglongjmp(3)跳过了。
....
内联代码通常由一条指令调整 堆栈指针,并且不检查堆栈溢出。因此,有 没有NULL错误返回。
.....
错误,如果不能显示堆栈帧,则没有没有错误指示 扩展。 (但是,分配失败后,该程序很可能会 如果尝试访问未分配的信号,则接收SIGSEGV 信号 在许多系统上,不能在列表的内部使用alloca() 函数调用的参数,因为由 alloca()会出现在堆栈中间 函数参数。
通过这种方式,异常不会导致alloca在堆栈帧中分配的内存泄漏。但是,这与任何异常一样,可能会导致堆内存泄漏,因为在alloca之后放置的析构函数和内存释放方法将被跳过。