内存管理,堆腐败和C++

Ber*_*ard 18 c++ memory heap stack

所以,我需要一些帮助.我正在用C++开发一个项目.但是,我想我已经设法破坏了我的堆.这是基于我std::string向一个类添加了一个并从另一个类中赋值的事实std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;
Run Code Online (Sandbox Code Playgroud)

使用堆栈转储在我的系统上崩溃.所以基本上我需要停下来查看我的所有代码和内存管理内容,找出我搞砸了的地方.代码库仍然很小(约1000行),因此这很容易实现.

不过,我对这种东西感到头疼,所以我想我会把它扔出去.我在一个Linux系统上并且已经四处乱窜valgrind,虽然我不知道我在做什么,但它确实报告说它std::string的析构函数是无效的.我不得不承认从谷歌搜索中获得"堆腐败"一词; 关于这类东西的任何通用文章也将受到赞赏.

(之前rm -rf ProjectDir,在C#再做一次:D)

编辑:我还没有说清楚,但我要求的是诊断这些记忆问题的建议.我知道std :: string的内容是正确的,所以这是我做过的事情(或者是一个bug,但是选择不是问题).我确信我可以查看我编写的代码,你很聪明的人会立即看到问题,但我想将这种代码分析添加到我的'工具箱'中,就像它一样.

Jos*_*osh 22

这些是可能解决问题的相对便宜的机制:

  1. 密切关注我的堆腐败问题 - 当他们动摇时,我正在更新答案.首先是平衡new[]delete[],但你已经这样做.
  2. valgrind带来更多的乐趣 ; 它是一个很好的工具,我只希望它可以在Windows下使用.我只减慢了你的程序减半,这与Windows等价物相比相当不错.
  3. 考虑使用Google Performance Tools作为替代malloc/new.
  4. 你清理了所有目标文件并重新开始了吗?也许你的make文件是......"次优"
  5. assert()的代码还不够.我怎么知道没见过它?就像使用牙线一样,assert()在他们的代码中没有人足够.为对象添加验证函数,并在方法开始和方法结束时调用它.
  6. 你在编译墙吗?如果没有,请这样做.
  7. 找一个像PC-Lint这样的棉绒工具.像您这样的小应用程序可能适合PC-lint演示页面,这意味着您无需购买!
  8. 删除它们后,检查是否为NULL.没有人喜欢悬垂的指针.与已声明但未分配的指针相同的演出.
  9. 停止使用数组.请改用矢量.
  10. 不要使用原始指针.使用智能指针.不要用auto_ptr!那件事......令人惊讶; 它的语义很奇怪.相反,选择一个Boost智能指针,或Loki库之外的东西.

  • +1,好名单!但是,我会对#8提出异议 - 虽然它可以防止'糟糕'的访问,但实际上是一种代码味道,在我的经验中隐藏了糟糕的逻辑或糟糕的对象生命周期管理...... (2认同)

Ric*_*den 10

我们曾经有一个错误,它避开了所有常规技术,valgrind,purify等.崩溃只发生在具有大量内存且仅在大型输入数据集上的机器上.

最终我们使用调试器观察点来追踪它.我将尝试在此描述该过程:

1)找出失败的原因.从您的示例代码看,"exampleString"的内存已损坏,因此无法写入.让我们继续这个假设.

2)在最后一个已知位置设置一个断点,使用或修改"exampleString"没有任何问题.

3)将监视点添加到'exampleString'的数据成员中.使用我的g ++版本,字符串存储在_M_dataplus._M_p.我们想知道此数据成员何时更改.GDB技术是:

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3
Run Code Online (Sandbox Code Playgroud)

我显然在这里使用带有g ++和gdb的linux,但我相信大多数调试器都可以使用内存观察点.

4)继续,直到触发观察点:

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where
Run Code Online (Sandbox Code Playgroud)

gdb where命令将给出一个返回跟踪,显示导致修改的原因.这是一个完全合法的修改,在这种情况下只是继续 - 或者如果你很幸运,它将是由于内存损坏的修改.在后一种情况下,您现在应该能够查看真正导致问题的代码,并希望能够修复它.

我们的bug的原因是具有负索引的数组访问.索引是一个指向'int'的指针的结果,模数是数组的大小.valgrind等人错过了这个漏洞.因为在这些工具下运行时分配的内存地址从来都不是" > MAX_INT",所以从来没有导致负索引.


Der*_*ark 7

哦,如果你想知道如何调试问题,这很简单.首先,得到一只死鸡.然后,开始摇晃它.

说真的,我还没有找到一种一致的方法来跟踪这些类型的错误.因为存在很多潜在的问题,所以没有一个简单的清单要经过.但是,我建议如下:

  1. 熟悉调试器.
  2. 在调试器中开始转向,看看你是否能找到任何看起来很可疑的东西.检查一下,看看exampleString = hello;行中发生了什么.
  3. 检查以确保它实际上exampleString = hello;在线路上崩溃,而不是在退出某些封闭块时(这可能导致析构器触发).
  4. 检查你可能正在做的任何指针魔术.指针算术,铸造等
  5. 检查所有分配和解除分配以确保它们匹配(没有双重释放).
  6. 确保您没有返回任何引用或指向堆栈上对象的指针.

还有很多其他的事情要尝试.我相信其他人也会提出想法.