-->

检测内存泄漏在参考计算的对象(Detecting memory leak in reference

2019-09-23 22:29发布

我想打印在哪一行AddRef和Release是called.Here是代码

在下面的代码我已经在ReferenceCount类,其主要功能,以增加和减少refernce计数创建。 Referencemanager此类跟踪的引用计数,一旦它到达0删除的对象。

Test1的是测试类。在主我创建的Test1指针与CReferenceManager类包装它。 现在创建CReferenceManager类的AddRef的过程中被调用,同时破坏释放将被调用。

如果有内存泄漏那么这将是更容易发现,如果我可以打印出的文件和行号时AddRef和与在这一点上引用计数释放调用。

如果有,我可以打印从那里AddRef和Release被调用文件和行号的方式。 一种方法是,我可以覆盖AddRef和在派生​​类和prinf文件和行号发布

//ReferenceCount.h
#include <string>
#include <Windows.h>

using namespace std;
class CReferenceCount
{
public:
   CReferenceCount();
   virtual ~CReferenceCount();
   virtual void AddRef();
   virtual bool Release();


private:
   LONG m_ref;

};


// RefCount.cpp 
//

#include "stdafx.h"
#include "ReferenceCount.h"


CReferenceCount::CReferenceCount():m_ref(0)
{
   AddRef();

}

CReferenceCount::~CReferenceCount()
{
}

void CReferenceCount::AddRef()
{
    InterlockedIncrement(&m_ref);
}

bool CReferenceCount::Release()
{
   if (InterlockedDecrement(&m_ref) == 0)
   {
      delete this;
      return true;
   }

   return false;
}



//ReferenceManager.h
#include <string>
#include <Windows.h>

using namespace std;
class CReferenceCount
{
public:
   CReferenceCount();
   virtual ~CReferenceCount();
   virtual void AddRef();
   virtual bool Release();


private:
   LONG m_ref;

};

//test.cpp
#include "stdafx.h"
#include "ReferenceCount.h"
#include "RefManager.h"
#include <iostream>
using namespace std;

class Test1: public CReferenceCount
{
public:
    Test1(){}
    ~Test1(){}

private :
    int m_i;
};

void main()
{
    Test1 *pTest= new Test1();
    CReferenceManager<Test1> testRef(pTest);

}

我已经发布Similare问题发现谁通过智能指针创建对象 设计模式来检测内存泄漏的引用计数的智能指针

但非的答案给予解释的权利来解决这个proble,

Answer 1:

唯一的办法是自己定义宏调用AddRef和Release,因为没有办法的职能,从他们被称为内部哪里知道。 所以,你可以使用类似。

#define RELEASE(obj) cout << __LINE__ << ":" << __FILE__ << endl; (obj).Release();

此外,不同的编译器具有不同的预先定义的宏; 如果便携性是一个问题,它的东西,你应该看看写像上面的代码时进入。 MSDN参考(2003)

下面给出您的意见,我会提供了另一种有点hackish的解决方案。 你可能无法看到您的参考被释放在那里,但你可以得到约在创建它,并没有被正确释放更多的信息。

template <typename T>
struct CReferenceManager
{
    CReferenceManager(const T & _obj, const string & _file, int _line) : mObj(_obj), mFile(_file), mLine(_line)
    {
        cout << "Constructing from " << _file << ":" << _line << endl;
        CReferenceManager::sObjects[make_pair(mFile, mLine)]++;
        mObj.addRef();
    }

    ~CReferenceManager()
    {
        cout << "Destructing object created at " << mFile << ":" << mLine << endl;
        CReferenceManager::sObjects[make_pair(mFile, mLine)]--;
        mObj.Release();
    }

    static map<pair<string, int>, int> sObjects;
    string mFile;
    int mLine;
    T obj;
}

int main()
{
...
    // Cycle through sObjects before return, note any unreleased entries
    return 0;
}

注意:这仅仅是伪代码; 我怀疑它编译或开箱的!



Answer 2:

你不应该分配或在自己的代码显式释放的引用,因此存储,但均递增或递减是不会帮你在所有的源文件和行,因为这些会(应该!)总是引用计数管理内码。

你没有包含源代码,您CReferenceManager类,但根据您的描述它是一个包装的引用计数的对象。 这个对吗? 正确执行这一CReferenceManager对象应确保:

  • 这需要一个裸指针存储指针,并且不更改引用计数一个构造函数(因为你CReferenceCount类一个参考对象创建)
  • 引用总是在析构函数递减
  • 引用在拷贝构造递增
  • 用于右侧对象引用被递增,以及用于左侧的对象引用在赋值运算符被递减
  • 没有明确的递增/递减的参考方法予以曝光
  • 操作 - >()方法应该将指针返回到对象
  • 应该从拥有它CReferenceManager实例中分离的引用计数的对象没有直接的方法。 的唯一方法是通过一个新的参考计数的对象的分配。

此外,你想使AddRef()Release()在你的CReferenceCount类方法私有,使他们只能访问通过类友谊CReferenceManager类。

如果你按照上面的规则,你CReferenceManager类,那么你可以通过确保每个人都通过访问在栈上分配的CReferenceManager包装物,防止泄漏或其他内存问题。 换一种说法:

创建一个新的引用计数对象,通过新创建的对象(具有一个参考)到堆栈分配CReferenceManager对象。 例:

CReferenceManager<Test1> testRef(new Test1());

传递物体作为参数传递给另一函数或方法,总是通过值传递CReferenceManager对象(未用参考,而不是由指针)。 如果你做这样的拷贝构造函数和析构函数将保持引用计数为你的照顾。 例:

void someFunction(CReferenceManager<Test1> testObj)
{
    // use testObj as if it was a naked pointer
    // reference mananagement is automatically handled
    printf("some value: %d\n", testObj->someValue());
}

int main()
{
    CReferenceManager<Test1> testRef(new Test1());
    someFunction(testRef);
}

如果你需要坚持的引用计数对象在一个容器中,然后插入由值CReferenceManager包装(而不是它的指针,而不是对象的裸体指针)。 例:

std::vector< CReferenceManager<Test1> > myVector;
CReferenceManager<Test1> testRef(new Test1());
myVector.push_back(testRef);
myVector[0]->some_method(); // invoke the object as if it was a pointer!

我相信,如果你严格按照上述规则,你会发现唯一的问题是在你的引用计数实现的bug。

遵循这些规则的一个例子实现在这个页面 ,但这种解决方案缺乏对多线程保护的任何支持。

我希望这有帮助!



Answer 3:

有这样的一些方法,但首先让我问你一两件事。 为什么你要用手来管理引用,并提供内存泄漏的机会? 你可以很容易地使用boost::intrusive_ptr为你做这项工作?(如果你不想要的提升,是没有问题的,请参阅实施intrusive_ptr并实现自己的类或者只是将其复制到自己的文件),然后你没有内存泄漏寻找它!

至于你的问题的答案,你可以有2 AddRef/Release一个用于调试版本,另一个版本,你应该添加AddRef位置的结构类似std::stackRelease从弹出他们stack ,并在结尾处,您怎么看从巫位置参考很多留在堆栈! 但如果这是COM实现记住,COM可以调用AddRef多的时间,然后在以后的时间将其删除,因此你无法理解其中AddRef有没有相应的Release



Answer 4:

对于项目我参与我有类似的需求。 我们有我们自己的智能指针模板类和不时内存泄漏出现由于循环引用。

要知道智能指针引用泄漏的对象仍然是活的(2个或更多),我们编译一个特殊的预处理器的源定义使特殊的调试代码的智能指针实现。 你可以看看我们的智能指针类 。

在本质上,每个智能指针和引用计数的对象获得一个唯一的ID。 当我们得到泄漏对象ID(通常使用的valgrind,以确定泄漏对象的内存分配的源位置),我们使用特殊的调试代码来获得其引用对象的所有智能指针标识。 然后,我们用一个配置文件,我们写下了智能指针的ID,并在下一应用程序启动时,这个文件是由我们的调试工具,就知道了其新建立的智能指针比如,它应该触发信号进入读调试器。 这揭示了在其中创建的是智能指针实例的堆栈跟踪。

诚然,这涉及到一些工作,可能只还清了较大的项目。

另一种可能是记录您的内部堆栈跟踪AddRef在运行时的方法。 看看我的ctkBackTrace类在运行时创建一个堆栈跟踪。 它应该很容易更换Qt的特定类型由标准STL类型。



Answer 5:

我想这与一些工作,并使用libunwind你很可能设法得到你所需要的(这将是一个真正的赞赏)。

http://www.nongnu.org/libunwind/docs.html



Answer 6:

引用计数的原理是增加计数器,当用户链接到对象,当它们断开链接降低。

所以,你必须:

  • 操纵智能指针,不是指针,使增大/减小透明
  • 超负荷的拷贝构造函数和smart_pointer的赋值运算符

符号为例:

  • A a = new A(); 引用计数= 0,没有人使用它
  • Link<A> lnk( a ); 引用计数= 1
  • obj.f( lnk ); OBJ存储LNK,引用次数= 2
  • 这种方法可能由于股权收益已经转移到obj

所以,看看传递参数(可以做自动拷贝),在拷贝到异物。

好的教程上存在,在CORBA星云。

您也可以看到ACE或ICE ,或0MQ 。



Answer 7:

做你的要求的一种方式,是通过AddRef和使用类似本次发布这样的信息:

void CReferenceCount::AddRef(const char *file=0, int line=-1) { if (file) cout << "FILE:" << file; if (line>0) count << " LINE: " << line;   .... do the rest here ... }

然后当你调用该函数,你可以使用类似于罗利上述建议,像这样的宏:

#define ADDREF(x) x.AddRef(__FILE__, __LINE__)

这将通过文件和行,其中调用时,我相信这是你问什么。 你可以控制你想用方法中的信息做什么。 打印出来,就像我上面那样,仅仅是一个例子。 你可能想收集超出了更多的信息,并登录到另一个对象,所以你有你的电话的历史,它们写入日志文件等,您也可以从呼叫点传递更多信息,不仅仅是文件和线,根据类型和跟踪你需要的水平。 默认的参数还允许您使用它们没有传递任何(通过一个简单的宏重新定义),正好看到最终版本将如何表现,有两个在压入堆栈和两个条件检查的开销。



Answer 8:

简短的回答:你应该使用他人发布的,即利用ADD / RELEASE宏并通过预定义的__FILE__和__LINE__宏编译器提供给您的跟踪类的想法。

稍微长一点的回答:您还可以使用功能,使您走栈,看看谁调用的函数,这是有点比使用宏更灵活,干净,但几乎可以肯定慢。

本页说明了如何使用GCC时实现这一点: http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/ 。

在Windows中,你可以使用一些编译器内在与符号查找功能一起。 有关详情请查看: http://www.codeproject.com/tools/minidump.asp

请注意,在这两种情况下,你的程序将需要至少包括一些符号这个工作。

除非你有在运行时做此特殊要求,我建议你检查了简短的回答。



文章来源: Detecting memory leak in reference counted objects