2

我有一个实现引用计数机制的对象。如果对它的引用次数为零,则删除该对象。

我发现我的对象永远不会被删除,即使我完成了它。这会导致内存过度使用。我所拥有的只是对对象的引用数量,我想知道引用它的位置,以便我可以编写适当的清理代码。

有什么方法可以在不必在源文件中使用 grep 的情况下完成此操作?(那会很麻烦。)

4

5 回答 5

6

在 C++ 中正确完成引用计数(引用计数)的很大一部分是使用资源分配即初始化,因此意外泄漏引用要困难得多。但是,这并不能通过引用计数解决所有问题。

也就是说,您可以在引用计数中实现一个调试功能,以跟踪持有引用的内容。然后,您可以在必要时分析此信息,并将其从发布版本中删除。(使用与使用 DEBUG 宏的目的相似的配置宏。)

您应该如何实现它取决于您的所有要求,但有两种主要方法可以做到这一点(简要概述差异):

  • 存储被引用对象本身的信息
    • 可从您的调试器访问
    • 更容易实施
  • 每次获取或释放引用时输出到特殊的跟踪文件
    • 程序退出后仍然可用(甚至异常)
    • 可以在程序运行时使用,无需在调试器中运行
    • 甚至可以在特殊的发布版本中使用并发送回给您进行分析

知道什么在引用给定对象的基本问题通常很难解决,并且需要一些工作。比较:你能告诉我每个知道你的邮政地址或电话号码的人和企业吗?

于 2010-01-10T08:22:52.933 回答
2

引用计数的一个已知弱点是它在存在循环引用时不起作用,即(在最简单的情况下)一个对象引用另一个对象,而另一个对象又引用前一个对象。这听起来像一个非问题,但在数据结构中,例如具有对父节点的反向引用的二叉树,就可以了。

如果您没有在引用的(未释放的)对象中明确提供“反向”引用列表,我看不出有办法找出谁在引用它。

在以下建议中,我假设您不想修改您的源代码,或者如果是的话,只是一点点。

您当然可以遍历整个堆/freestore 并搜索未释放对象的内存地址,但如果它的地址出现,则不能保证它实际上内存地址引用;它也可以是任何其他随机浮点数。但是,如果找到的值位于应用程序为一个对象分配的内存块内,那么它确实是指向另一个对象的指针的可能性会有所提高。

对这种方法的一个可能的改进是修改您使用的内存分配器——例如你的全局内存分配器operator new——以便它保留所有已分配内存块及其大小的列表。(在此的完整实现中,operator delete将删除已释放内存块的列表条目。)现在,在程序结束时,您知道在哪里搜索未释放对象的内存地址,因为您有您的程序实际使用的内存块列表。

老实说,上述建议对我来说听起来不太可靠;但也许定义一个自定义的全局operator newoperator delete这会在正确的方向上进行一些日志记录/跟踪以解决您的问题。

于 2010-01-10T08:27:33.720 回答
1

我假设您有一些具有 sayaddRef()release()成员函数的类,并且当您需要增加和减少每个实例的引用计数时调用它们,并且导致问题的实例在堆上并使用原始指针引用。最简单的解决方法可能是将所有指向受控对象的指针替换为boost::shared_ptr. 这非常容易做到,并且应该使您能够省去自己的引用计数——您可以让我提到的那些函数什么都不做。代码中所需的主要更改是传递或返回指针的函数的签名。其他需要更改的地方是初始化器列表(如果将指针初始化为 null)和 if() 语句(如果将指针与 null 进行比较)。更改指针的声明后,编译器会找到所有这些位置。

如果您不想使用shared_ptr- 也许您想保留类固有的引用计数 - 您可以制作自己的简单智能指针来处理您的类。然后用它来控制你的类对象的生命周期。因此,例如,不是使用原始指针完成指针分配并且您“手动”调用addRef(),您只需对包含addRef()自动的智能指针类进行分配。

于 2010-01-10T13:28:33.317 回答
0

我认为如果不更改代码就不可能做某事。例如,通过代码更改,您可以记住增加引用计数的对象的指针,然后查看剩下的指针并在调试器中检查它。如果可能 - 存储更详细的信息,例如对象名称。

于 2010-01-10T08:22:05.930 回答
0

我已经为我的需要创建了一个。您可以将您的代码与此代码进行比较,看看缺少什么。它并不完美,但它应该适用于大多数情况。 http://sites.google.com/site/grayasm/autopointer

当我使用它时,我会:

util::autopointer<A> aptr=new A();

我从不这样做:

A* ptr = new A();
util::autopointer<A> aptr = ptr; 

然后开始用 ptr 充实;这是不允许的。此外,我仅使用 aptr 来引用此对象。如果我错了,我现在有机会得到更正。:) 拜拜!

于 2010-01-10T09:44:56.357 回答