【问题标题】:My code crashes on delete this我的代码在删除时崩溃
【发布时间】:2011-11-18 22:30:15
【问题描述】:

尝试删除此内容时出现分段错误。

我知道你想删除这个,但它是我的前任留下的。我知道some precautions I should take,它已经过验证和处理。

我不知道什么样的条件可能会导致这次崩溃,只是偶尔会发生一次。大约 95% 的情况下,代码运行良好,但有时这似乎以某种方式损坏并崩溃。

类的析构函数不做任何事情。

我是否应该假设某些东西在其他地方破坏了我的堆,并且 this 指针以某种方式搞砸了?

编辑:根据要求,崩溃代码:

long CImageBuffer::Release()
{
  long nRefCount = InterlockedDecrement(&m_nRefCount);
  if(nRefCount == 0)
  {
    delete this;
  }
  return nRefCount;
}

对象是用一个新对象创建的,它不在任何类型的数组中。

【问题讨论】:

  • “当我这样做时会很痛” 然后不要这样做......或者显示更多有用的信息以获得更有用的答案。 (代码、堆栈等)
  • 一些关于 COM 对象析构函数的新旧博客条目可能会有所帮助:1、2、3。
  • InterlockedDecrement 部分困扰着我:您的对象是否存在于多个线程中? m_nRefCount 是否正确对齐LONG?
  • 使用InterlockedDecrement 只会使递减原子。您需要减量和 if 语句中的测试是原子的。
  • @JoeGauterin:嗯?为什么测试需要是原子的?重要的是它只被删除一次,对吧?计数达到零后多久删除对象都没有关系?

标签: c++ segmentation-fault destructor delete-operator self-destruction


【解决方案1】:

最明显的答案是:不要删除这个。

如果你坚持这样做,那么使用常见的方法来查找错误:
1.使用valgrind(或类似工具)查找内存访问问题
2. 编写单元测试
3. 使用调试器(准备长时间盯着屏幕看——取决于你的项目有多大)

【讨论】:

    【解决方案2】:

    您的new 和delete 似乎不匹配。请注意,delete this; 只能用于使用 new 分配的对象(并且在覆盖 operator new 或 C++ 运行时的多个副本的情况下,与 delete 匹配的特定 new 在当前范围)

    【讨论】:

    • 这是一种信仰的飞跃,不是吗?如果那是他的问题,我会感到震惊。与无与伦比的新/删除对相比,它更有可能成为内存垃圾......
    • @Goz:这包括 pmr 提到的情况,即具有静态或自动生命周期的对象,这是更可能的情况之一。
    • 我仍然认为他更有可能在沿线的某个地方丢弃他的堆,从而得到 dealloc 错误。我已经看到人们犯这种错误太多次了(然后责怪我,因为他们通过我的 DMA 链写入,您无法在写入后检查错误)
    • @Goz:这就是水晶球调试的问题吧?不同的球给出不同的解释。
    • @Eric:打印出(或记录)new返回的指针,以及传递给delete的指针。
    【解决方案3】:

    释放时的崩溃可能很痛苦:它不应该发生,而且一旦发生,代码太复杂而无法轻易找到解决方案。

    注意:InterlockedDecrement 的使用让我假设您在 Windows 上工作。

    记录一切

    我自己的解决方案是大量记录构造/破坏,因为在调试时崩溃很可能永远不会发生:

    1. 记录构造,包括this指针值和其他相关数据
    2. 记录销毁,包括this指针值和其他相关数据

    这样,您将能够查看this 是否被释放了两次,甚至被完全分配了。

    ...一切,包括堆栈

    我的问题发生在托管 C++/.NET 代码中,这意味着我可以轻松访问堆栈,这是一件幸事。您似乎在使用纯 C++,因此检索堆栈可能是一件苦差事,但它仍然非常有用。

    您应该尝试从 Internet 加载代码以打印出每个日志的当前堆栈。我记得为此玩过http://www.codeproject.com/KB/threads/StackWalker.aspx。

    请注意,您需要在调试版本中,或者将 PDB 文件与可执行文件一起提供,以确保堆栈将被完全打印。

    ...一切,包括多次崩溃

    我相信您使用的是 Windows:您可以尝试捕获 SEH 异常。这样,如果发生多次崩溃,您将看到所有崩溃,而不是只看到第一次,并且每次您都可以在日志中标记“OK”或“CRASHED”。我什至使用地图来记住分配/解除分配的地址,从而组织日志以将它们一起显示(而不是按顺序显示)。

    我在家,所以我无法为您提供确切的代码,但在这里,Google 是您的朋友,但要记住的是,您不能拥有 __try/__excepthanddler无处不在(C++ 展开和 C++ 异常处理程序与 SEH 不兼容),因此您必须编写一个中间函数来捕获 SEH 异常。

    你的崩溃线程相关吗?

    最后但并非最不重要的一点是,“我只发生在 5% 的时间”症状可能是由不同的代码路径执行引起的,或者是由于多个线程一起处理相同的数据。

    InterlockedDecrement 部分困扰着我:您的对象是否存在于多个线程中?并且 m_nRefCount 是否正确对齐和 volatile LONG?

    正确对齐和LONG 部分在这里很重要。

    如果您的变量不是LONG(例如,它可能是size_t,在64 位Windows 上不是LONG),那么该函数可能会以错误的方式工作。

    对于未在 32 字节边界上对齐的变量也可以这样说。您的代码中有#pragma pack() 指令吗?您的项目文件是否更改了默认对齐方式(我假设您正在使用 Visual Studio)?

    对于volatile 部分,InterlockedDecrement 似乎会生成读/写内存屏障,因此volatile 部分不应该是强制性的(请参阅http://msdn.microsoft.com/en-us/library/f20w0x5e.aspx)。

    【讨论】:

    • 我在 Linux 上工作,但我的前任来自 Windows 背景并在 Linux 上重新实现了 IRefObj 和 InterlockedDecrement 之类的东西。是的,我的想法正是
    • @Eric:o.O。 . .好的,所以它确实使答案复杂化了...... :-D ...无论如何,如果你有多个线程,那么 InterlockedDecrement 背后的 Linux API 很可能需要一个 volatile 以及一个正确对齐的正确大小的值(除非你的前任只是把锁放在那里......嗯)......至于堆栈,那么我想一定有某种Linux的堆栈浏览器代码。对于崩溃处理程序,我对 Linux 的了解不足,无法为您指出正确的解决方案(SIGSEGV 信号?)...您认为类似 COM 的引用计数对象实现对于您的项目是必要的吗?跨度>
    猜你喜欢
    • 2021-12-18
    • 1970-01-01
    • 2016-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多