【问题标题】:Unexpected Behaviour from tcmalloc来自 tcmalloc 的意外行为
【发布时间】:2013-05-16 09:47:05
【问题描述】:

我已经在一个大型项目中使用 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 保持应用程序运行有某种洞察力的解释?

提前致谢:)

祝你有美好的一天

【问题讨论】:

  • 他们选择了“未定义的行为”来不崩溃。我会依赖这个吗? 见鬼

标签: c++ linux libc tcmalloc


【解决方案1】:

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

好吧,我不同意这里,它完全错误的,因为你调用了 UB,任何事情都可能发生。

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

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

【讨论】:

  • 嗯...我不敢说它“完全错误”,因为你永远不知道程序员试图完成什么样的肮脏伎俩。也许世界上有些人声称他们必须做这样的事情。就我而言,这绝对是完全错误的,修复有问题的代码可以解决所有问题。尽管如此,我和其他人还是花了 7 天时间进行毫无意义的调查 -.-'
  • @BaroneAshura:即使在这种情况下,我个人也会认为这是完全错误的。顺便提一句。通过 valgrind 运行您的代码会在现场发现这一点。
  • 几个月前,当我们寻找内存泄漏时,这是一个选项......不幸的是,我们无法在 valgrind 环境中成功运行我们的应用程序 :( 无论如何,我同意你的看法它“完全错误”......我只是不想让任何人跳到我身上,说这不是“完全错误”:P
【解决方案2】:

首先,在您的情况下没有双重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 崩溃。

【讨论】:

    猜你喜欢
    • 2015-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多