0

我已经在一个大型项目中使用 tcmalloc 几个月了,到目前为止,我必须说我对它非常满意,最重要的是它的 HeapProfiling 功能允许跟踪内存泄漏并删除它们。

在过去的几周里,虽然我们在应用程序中遇到了随机崩溃,但我们找不到随机崩溃的根源。在一个非常特殊的情况下,当应用程序崩溃时,我们发现自己的某个应用程序线程的堆栈完全损坏。有几次我发现线程卡在 tcmalloc::PageHeap::AllocLarge() 中,但由于我没有链接 tcmalloc 的调试符号,我无法理解问题所在。

经过近一周的调查,今天我尝试了最简单的事情:从链接中删除tcmalloc以避免使用它,只是为了看看发生了什么。嗯...我终于发现了问题所在,并且有问题的代码看起来很像这样:

   void AllocatingFunction()
   {
       Object object_on_stack;
       ProcessObject(&object_on_stack);

   }

   void ProcessObject(Object* object)
   {
       ...
       // Do Whatever
       ...
       delete object;
   }

使用 libc 应用程序仍然崩溃,但我终于看到我正在对分配在堆栈上的对象调用 delete。

我仍然无法弄清楚为什么 tcmalloc 会保持应用程序运行,而不管这种非常危险(如果不是完全错误)的对象释放,以及当 AllocatingFunction 结束时 object_on_stack 超出范围时的双重释放。事实是,可以重复调用有问题的代码,而没有任何潜在可憎的暗示。

我知道如果使用不当,内存释放是那些“未定义的行为”之一,但令我惊讶的是“标准”libc 和 tcmalloc 之间的行为如此不同。

有没有人对为什么 tcmalloc 保持应用程序运行有某种洞察力的解释?

提前致谢 :)

祝你今天过得愉快

4

2 回答 2

2

非常危险(如果不是完全错误的话)对象释放

好吧,我在这里不同意,这完全错误的,而且由于您调用 UB,任何事情都可能发生。

这在很大程度上取决于 tcmalloc 代码在解除分配时的实际作用,以及它如何在该位置使用堆栈周围的(可能是垃圾)数据。

我也见过 tcmalloc 在这种情况下崩溃,以及 glibc 进入无限循环。你看到的只是巧合。

于 2013-05-16T10:00:34.907 回答
0

首先,你的情况没有双重free。当 object_on_stack 超出范围时,没有free调用,只是堆栈指针减少(或者更确切地说,随着堆栈增长......)。

其次,在删除过程中,TcMalloc 应该能够识别出栈中的地址不属于程序堆。这是free(ptr)实现的一部分:

const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
Span* span = NULL;
size_t cl = Static::pageheap()->GetSizeClassIfCached(p);

if (cl == 0) {
    span = Static::pageheap()->GetDescriptor(p);
    if (!span) {
        // span can be NULL because the pointer passed in is invalid
        // (not something returned by malloc or friends), or because the
        // pointer was allocated with some other allocator besides
        // tcmalloc.  The latter can happen if tcmalloc is linked in via
        // a dynamic library, but is not listed last on the link line.
        // In that case, libraries after it on the link line will
        // allocate with libc malloc, but free with tcmalloc's free.
        (*invalid_free_fn)(ptr);  // Decide how to handle the bad free request
        return;
    }

调用 invalid_free_fn 崩溃。

于 2013-07-05T22:20:37.433 回答