0

我正在维护一个似乎内存泄漏缓慢的遗留 C++ 应用程序。我已经设法通过确保当前配置不再引发任何异常来“修复”内存泄漏,并且我还可以触发泄漏并通过将其配置为导致许多异常来扩展它。

所有分配的内存都是使用 alloca() 而不是 malloc() 完成的。我对此给出的解释是,这是因为 alloca() 的工作方式类似于 java 垃圾收集器,并且在退出上下文时会自动释放内存。

由于泄漏是如此明显地与抛出的异常绑定,我有理论认为 alloca() 在抛出异常时无法释放内存。

这完全合理吗?如果是真的,我觉得它是 alloca() 的一个主要缺陷,但是当我用谷歌搜索 alloca() 时,它似乎通常是一个问题。

我会很感激任何专家的见解。

4

3 回答 3

3

问题很可能是由于某种程度的间接性造成的。

字面问题是“alloca如果抛出异常,是否返回内存?”。答案是;它只返回直接分配的内存。没有运行析构函数,并且alloca-allocated 内存中的任何拥有指针都被泄漏。

于 2018-12-14T10:43:44.297 回答
2

In C++ you should not be using the C memory management routines. In modern C++ the smart pointers provide you with fine grained deterministic garbage collection.

Though the space allocated via alloca() is probably deallocated with exceptions (because it is usually done by increasing the size of the current stack frame). This is not part of the standard and thus I don't think you can make any guarantees.

BUT This also means that any appropriate destructors on the object are never going to be called. Which means that if the object does its own memory management this will not be cleaned up (because the destructor is not run to clean it up).

Though alloca() is probably very fast. I think the extra burden it adds for memory management is (in the general case) not worth it; though if you have a special need for extra speed it may be worth it.

Code the looks like this:

void func()
{
    MyType* x = (MyType*)alloca(sizeof(MyType));

    passXtoCFunctionThatDoesNotTakeOwnership(x);
}

Should be written like this:

void func()
{
    std::unique_ptr<MyType> x = std::make_unique<MyType>();

    passXtoCFunctionThatDoesNotTakeOwnership(x.get());
}

If you are using it to hold an array of objects.

void func()
{
    MyType* x = (MyType*)alloca(sizeof(MyType) * arraySize);

    // STUFF
    x[0].stuff();
}

Then it is better to use a std::vector

void func()
{
    std::vector<MyType> x;
    x.reserve(arraySize);   // or resize() if that is appropriate

    // STUFF
    x[0].stuff();
}

If you are using it for simple objects. Then you should probably just be declaring automatic variables:

void func()
{
    MyType* x = (MyType*)alloca(sizeof(MyType));

    x->myData = 5;
}

You should just be declaring a variable:

void func()
{
    MyType x;

    x.myData = 5;
}
于 2018-12-14T10:15:07.437 回答
0

来自 Linux

……

因为 alloca() 分配的空间是在堆栈帧内分配的,所以如果函数返回被调用 longjmp(3) 或 siglongjmp(3) 跳过,该空间会自动释放。

……

内联代码通常由一条调整堆栈指针的指令组成,并且不检查堆栈溢出。因此,没有 NULL 错误返回。

......

错误如果堆栈帧无法扩展,则没有错误指示。(但是,在分配失败后,如果程序试图访问未分配的空间,它很可能会收到 SIGSEGV信号。)在许多系统上,alloca() 不能在函数调用的参数列表中使用,因为堆栈空间由 alloca() 保留的将出现在函数参数空间中间的堆栈上。

这样,异常就不会导致栈帧中alloca分配的内存发生内存泄漏。但是,与任何异常一样,这可能会导致堆内存泄漏,因为将跳过 alloca 之后放置的析构函数和内存释放方法。

于 2018-12-14T10:15:49.363 回答