来自tcmalloc的意外行为
Unexpected Behaviour from tcmalloc
我已经在一个大型项目中使用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不顾这种非常危险(如果不是完全错误的话)的对象释放而保持应用程序运行,以及当object_on_stack在AllocatingFunction结束时超出范围时的双重释放。事实上,那些令人讨厌的代码可以被反复调用,而没有任何潜在的令人讨厌的暗示。
我知道内存释放在使用不当时是那些"未定义的行为"之一,但我惊讶的是"标准"libc和tcmalloc之间的行为如此不同。
有没有人对为什么tcmalloc保持应用程序运行有一些解释?
Thanks in advance:)
祝你有美好的一天
非常危险(如果不是完全错误的话)的对象回收
好吧,我不同意,它是完全错误的,并且由于您调用了UB,任何事情都可能发生。
这在很大程度上取决于tcmalloc代码在释放时实际做了什么,以及它如何使用该位置堆栈周围的(可能是垃圾的)数据。
我也见过tcmalloc在这种情况下崩溃,以及glibc进入无限循环。你所看到的只是巧合。
首先,您的案例中没有双重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调用崩溃
- 在C++中对T*类型执行std::move的意外行为
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 处理除以零会导致<csignal>意外行为
- vscode下的Arduino代码出现意外编译错误
- 使用++运算符会导致意外的结果
- 套接字读取后,我在缓冲区中看到意外输入
- 更改.cpp程序的输入文件中数据的位置会意外更改输出
- 使用vscode调试时,GDB意外退出
- 此测试()中发生了什么意外过程?为什么总是覆盖 ch[0 1 2..]?
- 尝试将字符串/字符转换为整数会产生意外结果
- 错误 C2760:语法错误:映射迭代器上意外的标记"标识符",预期的";"
- C++标头错误 C2238 意外标记";"
- C++中意外的多头值
- vector.size() 在比较中意外工作
- 使用 malloc() 时出现意外大小
- 多线程程序中出现意外的内存泄漏
- 为什么static_cast基础类型的枚举类int8_t获得意外值?
- 字符串比较中的意外输出
- 我的代码中的意外价值以及我如何修复它
- 来自tcmalloc的意外行为