内存泄漏解决方法是真的么

2024-11-07 21:43:23
推荐回答(1个)
回答1:

内存泄露的问题其困难在于1.编译器不能发现这些问题。2.运行时才能捕获到这些错误,这些错误没有明显的症状,时隐时现。3.对于手机等终端开发用户来说,尤为困难。下面从三个方面来解决内存泄露:

第一,良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露。当程式稳定之后,在来检测内存泄露时,无疑增加了排除的困难和复杂度。

使用了内存分配的函数,要记得要使用其想用的函数释放掉,一旦使用完毕。

Heap memory:

malloc\realloc ------ free

new \new[] ---------- delete \delete[]

GlobalAlloc------------GlobalFree

要特别注意数组对象的内存泄漏

MyPointEX *pointArray =new MyPointEX [100];

其删除形式为:

delete []pointArray

Resource Leak :对于系统资源使用之前要仔细看起使用方法,防止错误使用或者忘记释放掉系统资源。

我们看MSDN上一个创建字体的例子:
RECT rect;

HBRUSH hBrush;
FONT hFont;
hdc = BeginPaint(hWnd, &ps);
hFont = reateFont(48,0,0,0,FW_DONTCARE,FALSE,TRUE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Impact"));

SelectObject(hdc, hFont);
SetRect(&rect, 100,100,700,200);

SetTextColor(hdc, RGB(255,0,0));
DrawText(hdc, TEXT("Drawing Text with Impact"), -1,&rect, DT_NOCLIP);

DeleteObject(hFont);
EndPaint(hWnd, &ps);

如果使用完成时候忘记释放字体,就造成了资源泄漏。

对于基于引用计数的系统对象尤其要注意,因为只有其引用计数为0时,该对象才能正确被删除。而其使用过程中有其生成的新的系统资源,使用完毕后,如果没有及时删除,都会影响其引用计数。

IDNS *m_pDns//define a DNS object.

If(NULL == m_pDns)

{
IEnv_CreateInstance (m_pEnv,AEECLSID_DNS,(void **) (&m_pDns))

}

If(m_pDns)
{

Char szbuff[256];

IDNS_AddQuestions(M_pDns,AEEDNSTYPE_A,ADDDNSCLASS_IN,szbuff);

IDNS_Start(m_pDns,this);

const AEEDNSResponse * pDnsResponse = NULL;

IDNS_GetResponse(pMe->m_pDns, &pDnsResponse);

…………………………………………………………
…………………………………………………………..

………………………………………………………..

}

DNS_Release(pMe->m_pDns);//当程序运行到此时,其返回值不是0,是1,其含义是程序已经产生内存泄露了,系统已经有一个由DNS所产生的内核对象没有释放,而当这段代码多次执行之后,内存泄露将不断增加……..

m_pDns=NULL;

}

看起来很不直观,仔细分析就会发现,对象pDnsResponse是从m_pDns产生新的object,所以m_pDns的引用计数会增加,因此在使用完pDnsResponse,应该release 该对象使其引用计数恢复正常。

对于资源,也可使用RAII,RAII(Resource acquisition is initialization)资源获取即初始化,它是一项很简单的技术,利用C++对象生命周期的概念来控制程序的资源,例如内存,文件句柄,网络连接以及审计追踪(audit trail)等.RAII的基本技术原理很简单.若希望保持对某个重要资源的跟踪,那么创建一个对象,并将资源的生命周期和对象的生命周期相关联.如此一来,就可以利用C++复杂老练的对象管理设施来管理资源.(有待完善)

例2:

Struct ITypeface *pTypeface;

if (pTypeface)

{

IANY_CreateInstance(g_pApplet->m_pIShell,AEECLSID_BTFETypeface,void**)& Typeface);

}

接下来我们就可以从这个接口上面创建字体,比如

IHFont **pihf=NULL;

ITypeface_NewFontFromFile(ITypeface,……,&pihf).

ITypeface_NewFontFrommemory(ITypeface,……..,&pihf)

ITypeface_NewFontFromClassID(IType,……,&pihf)

但是要切记,这些字体在使用完成后一定要release掉,否则最后 iTypeface的引用计数就是你最后没有删除掉的字体的个数。

第二,重载 new 和 delete。这也是大家编码过程中常常使用的方法。

下面给出简单的sample来说明。

memchecker.h

structMemIns

{

void * pMem;

int m_nSize;

char m_szFileName[256];

int m_nLine;

MemIns * pNext;

};

classMemManager

{

public:

MemManager();

~MemManager();

private:

MemIns *m_pMemInsHead;

int m_nTotal;

public:

static MemManager* GetInstance();

void Append(MemIns *pMemIns);

void Remove(void *ptr);

void Dump();

};

void *operatornew(size_tsize,constchar*szFile, int nLine);

void operatordelete(void*ptr,constchar*szFile, int nLine);

void operatordelete(void*ptr);

void*operatornew[] (size_tsize,constchar*szFile,int nLine);

void operatordelete[](void*ptr,constchar*szFile, int nLine);

void operatordelete[](void *ptr);

memechecker.cpp

#include"Memchecher.h"

#include

#include

#include

MemManager::MemManager()

{

m_pMemInsHead=NULL;

m_nTotal=NULL;

}

MemManager::~MemManager()

{

}

voidMemManager::Append(MemIns *pMemIns)

{

pMemIns->pNext=m_pMemInsHead;

m_pMemInsHead = pMemIns;

m_nTotal+= m_pMemInsHead->m_nSize;

}

voidMemManager::Remove(void *ptr)

{

MemIns * pCur = m_pMemInsHead;

MemIns * pPrev = NULL;

while(pCur)

{

if(pCur->pMem ==ptr)

{

if(pPrev)

{

pPrev->pNext =pCur->pNext;

}

else

{

m_pMemInsHead =pCur->pNext;

}

m_nTotal-=pCur->m_nSize;

free(pCur);

break;

}

pPrev = pCur;

pCur = pCur->pNext;

}

}

voidMemManager::Dump()

{

MemIns * pp = m_pMemInsHead;

while(pp)

{

printf( "File is %s\n", pp->m_szFileName );
printf( "Size is %d\n", pp->m_nSize );
printf( "Line is %d\n", pp->m_nLine );
pp = pp->pNext;
}
}
voidPutEntry(void *ptr,intsize,constchar*szFile, int nLine)

{
MemIns * p = (MemIns *)(malloc(sizeof(MemIns)));
if(p)

{
strcpy(p->m_szFileName,szFile);
p->m_nLine = nLine;
p->pMem = ptr;
p->m_nSize = size;
MemManager::GetInstance()->Append(p)

}
}
voidRemoveEntry(void *ptr)

{
MemManager::GetInstance()->Remove(ptr);
}
void *operatornew(size_tsize,constchar*szFile, int nLine)

{
void * ptr = malloc(size);
PutEntry(ptr,size,szFile,nLine);
return ptr;
}

voidoperatordelete(void *pt
{
RemoveEntry(ptr);
free(ptr);
}
void operatordelete(void*ptr,constchar * file, intline)
{
RemoveEntry(ptr);
free(ptr);
}
void*operatornew[] (size_tsize,constchar* szFile,intnLine)
{
void * ptr = malloc(size);
PutEntry(ptr,size,szFile,nLine);

return ptr;
}
void operatordelete[](void *ptr)
{
RemoveEntry(ptr);
free(ptr);
}
void operatordelete[](void*ptr,constchar*szFile,intnLine)
{
RemoveEntry(ptr);
free(ptr);
}
#definenewnew(__FILE__,__LINE__)

MemManagerm_memTracer;
MemManager*MemManager::GetInstance()
{
return &m_memTracer;
}

void main()
{
int *plen =newint ;
*plen=10;
delete plen;
char *pstr=newchar[35];
strcpy(pstr,"hello memory leak");
m_memTracer.Dump();
return ;
}

其主要思路是将分配的内存以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表,其中记录了内存泄露的文件,所在文件的行数以及泄露的大小哦。
第三,Boost 中的smart pointer(待完善,结合大家的建议)
第四,一些常见的工具插件,详见我的Blog中相关文章。

4. 由内存泄露引出内存溢出话题:

所谓内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是会产生内存溢出的问题。

常见的溢出主要有:

内存分配未成功,却使用了它。
常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)或if(p!=NULL)进行防错处理。

内存分配虽然成功,但是尚未初始化就引用它。
内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for 循环语句中,循环次数很容易搞错,导致数组操作越界。

使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。(这点可是深有感受,呵呵)

不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。