【问题标题】:What can cause delete to loop Sleep()ing and never return in the Borland / Embarcadero RTL?什么会导致 delete 循环 Sleep()ing 并且永远不会在 Borland / Embarcadero RTL 中返回?
【发布时间】:2013-02-24 07:01:33
【问题描述】:

我有一个奇怪的错误,当删除一个特定对象时,delete 永远不会返回。相反,它似乎花时间在一个永久循环中调用Sleep()。这是调用堆栈:

:7723fd71 ntdll.ZwDelayExecution + 0x15
:767c4498 KERNELBASE.Sleep + 0xf
:21495359 BORLNDMM.@Borlndmm@SysFreeMem$qqrpv + 0xb5
:32aaa3e5 CC32100MT._free + 0xd
:32a020b2 CC32100MT.@$bdele$qpv + 0x1e
[My code]

运行时,它返回到SysFreeMem(永远不会返回到free)并再次从SysFreeMem 内部调用Sleep,永久循环。

我的代码非常简单:它是一个 VCL 事件处理程序,一个旋转框刚刚更改,一个设置对象更新,它所做的临时设置正在被删除:

delete poNewSettings;

这段代码已经一两年没有改变了,设置类也有一段时间没有改变,它基本上是一个包含许多设置的巨大的类似结构的对象。它有一个编译器生成的析构函数。单步执行代码显示了一些删除字符串和其他 std:: 对象的调用,然后在单步执行下一个函数时(即在 ~basic_string() 的末尾,在右大括号 @987654330 处按 F7 @),IDE 从不显示。冻结是它在 Sleep() 中。此代码在主线程中运行。

我尝试使用 CPU 视图来推断调用 Sleep() 的延迟,但无法发现它 - 恐怕我还没有完全掌握 x86 汇编代码。

另一个线程在Sleep 内部,可能与 COM 相关(该应用程序是一个 COM 服务器,尽管目前没有使用 COM)。它的整个调用栈是:

:7723fd71 ntdll.ZwDelayExecution + 0x15
:767c4498 KERNELBASE.Sleep + 0xf
:74c7d98d ; C:\Windows\syswow64\ole32.dll
:74c7d87a ; C:\Windows\syswow64\ole32.dll
:768133aa kernel32.BaseThreadInitThunk + 0x12
:77259ef2 ntdll.RtlInitializeExceptionChain + 0x63
:77259ec5 ntdll.RtlInitializeExceptionChain + 0x36

另一个线程正在做某事并主动分配和释放内存并调用Sleep() - 一个后台任务正在运行,它正在检查它是否需要工作(它不必)并且正在休眠 100 毫秒以检查再次。分配和免费用于文件夹路径的字符串。虽然程序中还有其他线程,但它们都被阻塞等待做某事,这次使用WaitForSingleObject 或类似的。此后台任务线程每 100 毫秒唤醒一次(从睡眠中返回);我没有看到(还没有!)它如何影响另一个线程的delete

据我所知,没有其他线程正在删除或分配任何内容。我在CC32100MT._mallocCC32100MT._freeCC32100MT._realloc 中设置了断点。运行时没有其他线程中断,当暂停检查时,这些调用中没有其他线程。

删除循环而不返回的原因可能是什么?

我正在使用: * RAD Studio 2010,全面更新 * 应用程序主要是 C++ (C++ Builder) 和少量的 Delphi。 * 因为它使用的是 Embarcadero RTL,所以我认为它使用的是 FastMM

我添加了 Delphi 标记,因为它使用 Embarcadero RTL 和 FastMM,Delphi 人比 C++ 人使用更多。

【问题讨论】:

  • 听起来你可能在空闲堆块列表中出现了堆损坏,并出现了循环循环。我不熟悉 C++Builder - 你能从那里启用 FastMM 调试标志吗?
  • 感谢您的建议!我不确定 FastMM 调试模式。我认为它使用了 fastMM 的 C 实现——我之前在 RTL 源文件夹中看到过一个——而不是 Delphi 实现。坦率地说,我对内部结构了解得不够多。

标签: delphi memory sleep c++builder fastmm


【解决方案1】:

在这种情况下,我不能 100% 确定您是否可以信任 RAD Studio IDE 中的调试器来正确命名所涉及的内核符号。

我认为你应该进入发生这种情况的地方,然后检查你正在释放的变量,看看它是否已损坏。

如果您要释放的变量是您自己定义的类,我通常喜欢在对象的开头和结尾添加 Guard 字段。

class TMyPOSettings {
  public:
    ...
  private:
    int Guard1;
    // all my other instance data 
    int Guard2
};

在构造函数中,我将守卫设置为我在某处定义的 MAGIC1 和 MAGIC2, 在析构函数中,我将守卫设置为 MAGIC_FREE_1 和 MAGIC_FREE_2。

然后我可以查看我是否重复破坏了同一个对象,或者是否有其他东西覆盖了我在该位置的堆内存。

我想知道您正在调试的内容是否可能实际上是在 Windows 中捕获到严重异常后代码的结果,中止您的线程正在执行的任何操作,并且基本上现在什么都不做,直到您的进程终止。我要调试的第一个地方是单步执行析构函数的代码并查看它的作用。

【讨论】:

  • 谢谢沃伦。据我所知,它没有损坏(它只在上面创建了两行!)我将继续调查......
猜你喜欢
  • 2021-09-30
  • 2018-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-21
  • 1970-01-01
  • 1970-01-01
  • 2012-08-19
相关资源
最近更新 更多