C ++:在引用计数系统中调试内存泄漏

时间:2009-04-11 13:38:34

标签: c++ memory-leaks

我的一些应用程序中有一些引用计数类,还有那些应用程序使用的dll,都是从IRefCounted接口继承并实现的。

为了帮助找到这些内存泄漏的来源,我希望每个应用程序都能保留所有这些refrence计数类的列表。

问题是管理这些列表的实例,使得它们的使用不会影响我的类的使用(例如,我不需要一直将指针传递给列表,而是以某种方式将它附加到处理)。

- 很有可能这些应用程序中的一些可能同时运行,并且使用相同的dll。每个应用程序都需要自己的对象列表,该应用程序加载的所有dll等都需要使用该列表(但是请注意,一个dll可能由多个应用程序加载......)。 - 列表必须在应用程序中的每个其他全局变量和静态变量之后销毁,因此列表中被破坏的对象是未正确释放的对象。

然后我只需在列表的析构函数中添加一个断点,这样我就可以查看调试器中的任何未分配的对象。

2 个答案:

答案 0 :(得分:1)

我猜你正在使用COM。你可能需要找到一种方法来获得weak pointer,以便实例化对象的注册表不会阻止它们被毁坏。

如果可以修改所有类,则可以注入一个静态成员来跟踪所有实例,并让实例的析构函数从静态成员中删除。例如,您可以使用如下所示的基类或实用程序类:

class InstanceRegistry {
protected:
    InstanceRegistry() {
       registry.insert(this);
    }
    ~InstanceRegistry() {
       registry.remove(this);
    }
private:
  static SomeContainerType<InstanceRegistry*> registry;
};

如果您想为不同类型的类等使用不同的注册表,则需要进行额外的工作。

答案 1 :(得分:1)

如果进程使用相同的DLL,则每个进程都会获得该DLL的静态(或“全局”)数据的私有副本。

所以你需要做的就是使列表成为DLL中的全局变量,并从每个应用程序链接到该DLL。这样,就没有必要再传递任何其他内容了。

由于多DLL过程中对象破坏顺序的不可预测性,你想要捕获列表的破坏是很困难的。

mainWinMain函数末尾转储列表内容会更简单。

如果您没有以一致的方式使用智能指针类,那么请执行此操作。此外,可能值得寻找循环引用计数 - 对象A具有对象B的计数,反之亦然。这是未发布对象的常见原因。

<强>更新

要强制所有静态析构函数运行和释放对象,以便随后可以检查列表中的条目,您需要以某种方式构建应用程序。

假设您有一个非常小的EXE启动进程,并加载了许多实际完成所有工作的其他DLL。这些其他DLL以LoadLibrary加载,不知何故(可能是COM或类似COM的系统)。 LoadLibrary API通过将DLL加载到进程中来工作,或者如果DLL已经加载则递增DLL上的内部引用计数器。 FreeLibrary API会递减计数器,直到它达到零,然后卸载DLL(此时将执行该DLL的静态析构函数)。

为此,我们现在添加我们的诊断DLL,其中包含所有未完成的引用计数对象的列表。所有其他DLL都使用import-lib链接到诊断DLL,EXE也使用LoadLibrary。

main即将退出时,EXE将浏览先前加载的DLL句柄列表,并在所有这些句柄上调用FreeLibrary。通过保持加载诊断DLL,它确保它仍然在那里。至少这是理论。

但是应该以什么顺序卸载其他DLL?如果A.DLL具有指向B.DLL中定义的对象的静态指针,那么最好先卸载A.因此,您需要了解各种DLL如何构成“分层”体系结构,更高层依赖于较低层,从而为您提供安全的卸载顺序。

此外,一旦卸载了所有DLL,诊断列表中引用DLL中对象的任何条目现在将指向堆上的有效数据,但vtable将指向由现在已卸载的DLL,因此您将无法在这些对象上调用虚函数。您应该能够检查他们的数据。