我在一个大型项目中使用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能让应用程序保持运行?
提前感谢:)
祝您今天愉快
发布于 2013-05-16 18:00:35
非常危险(如果不是完全错误的话)对象释放
好吧,我不同意这一点,这是完全错误的,因为你调用了UB,任何事情都可能发生。
这在很大程度上取决于tcmalloc代码在释放时实际做了什么,以及它如何在该位置使用堆栈周围的数据(可能是垃圾)。
我也见过tcmalloc在这种情况下崩溃,以及glibc进入无限循环。你看到的只是巧合。
发布于 2013-07-06 06:20:37
首先,在你的例子中没有双重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的调用崩溃。
https://stackoverflow.com/questions/16584109
复制相似问题