释放时的崩溃可能很痛苦:它不应该发生,当它发生时,代码太复杂而无法轻易找到解决方案。
注意:使用InterlockedDecrement
让我假设您在 Windows 上工作。
记录一切
我自己的解决方案是大量记录构造/破坏,因为在调试时崩溃很可能永远不会发生:
- 记录构造,包括
this
指针值和其他相关数据
- 记录销毁,包括
this
指针值和其他相关数据
这样,您将能够查看 是否this
被释放了两次,甚至根本没有分配。
...一切,包括堆栈
我的问题发生在托管 C++/.NET 代码中,这意味着我可以轻松访问堆栈,这是一件幸事。您似乎在使用纯 C++,因此检索堆栈可能是一件苦差事,但它仍然非常有用。
您应该尝试从 Internet 加载代码以打印出每个日志的当前堆栈。我记得为此玩过http://www.codeproject.com/KB/threads/StackWalker.aspx。
请注意,您需要在调试版本中,或者在可执行文件中包含 PDB 文件,以确保堆栈将被完全打印。
...一切,包括多次崩溃
我相信您在 Windows 上:您可以尝试捕获 SEH 异常。这样,如果发生多次崩溃,您将看到所有崩溃,而不是只看到第一次,并且每次您都可以在日志中标记“OK”或“CRASHED”。我什至使用地图来记住分配/解除分配的地址,从而组织日志以将它们一起显示(而不是按顺序显示)。
我在家,所以我无法为您提供确切的代码,但在这里,Google 是您的朋友,但要记住的是,您不能到处都有__try
/处理程序(C++ 展开和 C++ 异常处理程序是__except
与 SEH 不兼容),因此您必须编写一个中间函数来捕获 SEH 异常。
你的崩溃线程是相关的吗?
最后但并非最不重要的一点是,“我只发生了 5% 的时间”症状可能是由不同的代码路径执行引起的,或者是由于多个线程一起处理相同的数据。
这InterlockedDecrement
部分让我很困扰:你的对象是否存在于多个线程中?m_nRefCount 是否正确对齐volatile
LONG
?
正确对齐和LONG
部分很重要,在这里。
如果您的变量不是 a LONG
(例如,它可能是 a ,这在 64 位 Windows 上size_t
不是 a ),那么该函数很可能会以错误的方式工作。LONG
对于未在 32 字节边界上对齐的变量也可以这样说。#pragma pack()
您的代码中有说明吗?您的项目文件是否更改了默认对齐方式(我假设您正在使用 Visual Studio)?
对于该volatile
部分,InterlockedDecrement
似乎会生成读/写内存屏障,因此该volatile
部分不应该是强制性的(请参阅http://msdn.microsoft.com/en-us/library/f20w0x5e.aspx)。