内存泄漏以及常见的解决方法
? 之所以撰寫(xiě)這篇文章是由于前段時(shí)間花費(fèi)了非常大的精力在已經(jīng)成熟的代碼上再去處理memory leak問(wèn)題。寫(xiě)此的目的是希望我們應(yīng)該養(yǎng)成良好的編碼習(xí)慣,盡可能的避免這種問(wèn)題,由于當(dāng)你對(duì)著一大片的代碼再去處理此類(lèi)的問(wèn)題,此時(shí)無(wú)疑添加了解決的成本和難度。準(zhǔn)確的說(shuō)屬于補(bǔ)救措施了。 1. 什么是內(nèi)存泄漏(memory leak)?
?指因?yàn)槭韬龌蝈e(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并不是指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因?yàn)樵O(shè)計(jì)錯(cuò)誤,失去了對(duì)該段內(nèi)存的控制,因而造成了內(nèi)存的浪費(fèi)。?
A memory leak is a particular type of unintentional memory consumption by a computer program where the program fails to release memory when no longer needed. This condition is normally the result of a bug in a program that prevents it from freeing up memory that it no longer needs.This term has the potential to be confusing, since memory is not physically lost from the computer. Rather, memory is allocated to a program, and that program subsequently loses the ability to access it due to program logic flaws.?
2. 對(duì)于C和C++這樣的沒(méi)有Garbage Collection 的語(yǔ)言來(lái)講,我們主要關(guān)注兩種類(lèi)型的內(nèi)存泄漏:
?? 堆內(nèi)存泄漏(Heap leak)。對(duì)內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過(guò)malloc,realloc new等從堆中分配的一塊內(nèi)存,再是完畢后必須通過(guò)調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計(jì)的錯(cuò)誤導(dǎo)致這部分內(nèi)存沒(méi)有被釋放,那么此后這塊內(nèi)存將不會(huì)被使用,就會(huì)產(chǎn)生Heap Leak.?
? 系統(tǒng)資源泄露(Resource Leak).主要指程序使用系統(tǒng)分配的資源比方 Bitmap,handle ,SOCKET等沒(méi)有使用對(duì)應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費(fèi),嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定。??
3. 怎樣解決內(nèi)存泄露?
內(nèi)存泄露的問(wèn)題其困難在于1.編譯器不能發(fā)現(xiàn)這些問(wèn)題。2.執(zhí)行時(shí)才干捕獲到這些錯(cuò)誤,這些錯(cuò)誤沒(méi)有明顯的癥狀,時(shí)隱時(shí)現(xiàn)。3.對(duì)于手機(jī)等終端開(kāi)發(fā)用戶來(lái)說(shuō),尤為困難。以下從三個(gè)方面來(lái)解決內(nèi)存泄露:
第一,良好的編碼習(xí)慣,盡量在涉及內(nèi)存的程序段,檢測(cè)出內(nèi)存泄露。當(dāng)程式穩(wěn)定之后,在來(lái)檢測(cè)內(nèi)存泄露時(shí),無(wú)疑添加了排除的困難和復(fù)雜度。
使用了內(nèi)存分配的函數(shù),要記得要使用其想用的函數(shù)釋放掉,一旦使用完成。
Heap memory:
malloc\realloc ------? free
new \new[] ----------? delete \delete[]
GlobalAlloc------------GlobalFree?
要特別注意數(shù)組對(duì)象的內(nèi)存泄漏
???? MyPointEX *pointArray =new MyPointEX [100];
????? 其刪除形式為:
???? delete []pointArray?
Resource Leak :對(duì)于系統(tǒng)資源使用之前要細(xì)致看起用法,防止錯(cuò)誤使用或者忘記釋放掉系統(tǒng)資源。
我們看MSDN上一個(gè)創(chuàng)建字體的樣例: ?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);?
假設(shè)使用完畢時(shí)候忘記釋放字體,就造成了資源泄漏。??? 對(duì)于基于引用計(jì)數(shù)的系統(tǒng)對(duì)象尤其要注意,由于僅僅有其引用計(jì)數(shù)為0時(shí),該對(duì)象才干正確被刪除。而其使用過(guò)程中有其生成的新的系統(tǒng)資源,使用完成后,假設(shè)沒(méi)有及時(shí)刪除,都會(huì)影響其引用計(jì)數(shù)。
?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);//當(dāng)程序執(zhí)行到此時(shí),其返回值不是0,是1,其含義是程序已經(jīng)產(chǎn)生內(nèi)存泄露了,系統(tǒng)已經(jīng)有一個(gè)由DNS所產(chǎn)生的內(nèi)核對(duì)象沒(méi)有釋放,而當(dāng)這段代碼多次執(zhí)行之后,內(nèi)存泄露將不斷添加……..
m_pDns=NULL;
? }
看起來(lái)非常不直觀,細(xì)致分析就會(huì)發(fā)現(xiàn),對(duì)象pDnsResponse是從m_pDns產(chǎn)生新的object,所以m_pDns的引用計(jì)數(shù)會(huì)添加,因此在使用完pDnsResponse,應(yīng)該release 該對(duì)象使其引用計(jì)數(shù)恢復(fù)正常。 ? 對(duì)于資源,也可使用RAII,RAII(Resource acquisition is initialization)資源獲取即初始化,它是一項(xiàng)非常easy的技術(shù),利用C++對(duì)象生命周期的概念來(lái)控制程序的資源,比如內(nèi)存,文件句柄,網(wǎng)絡(luò)連接以及審計(jì)追蹤(audit trail)等.RAII的基本技術(shù)原理非常easy.若希望保持對(duì)某個(gè)重要資源的跟蹤,那么創(chuàng)建一個(gè)對(duì)象,并將資源的生命周期和對(duì)象的生命周期相關(guān)聯(lián).如此一來(lái),就能夠利用C++復(fù)雜老練的對(duì)象管理設(shè)施來(lái)管理資源.(有待完好)?
例2:
Struct ITypeface *pTypeface;
if (pTypeface)
{
IANY_CreateInstance(g_pApplet->m_pIShell,AEECLSID_BTFETypeface,void**)& Typeface);
}
接下來(lái)我們就能夠從這個(gè)接口上面創(chuàng)建字體,比方
IHFont **pihf=NULL;
?? ITypeface_NewFontFromFile(ITypeface,……,&pihf).
?? ITypeface_NewFontFrommemory(ITypeface,……..,&pihf)
? ?ITypeface_NewFontFromClassID(IType,……,&pihf)
?
?? 可是要切記,這些字體在使用完畢后一定要release掉,否則最后 iTypeface的引用計(jì)數(shù)就是你最后沒(méi)有刪除掉的字體的個(gè)數(shù)。?
第二,重載? new 和 delete。這也是大家編碼過(guò)程中常用的方法。
以下給出簡(jiǎn)單的sample來(lái)說(shuō)明。
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<stdio.h>
#include<malloc.h>
#include<string.h>
?
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 *ptr)
{
??? 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)
?{
轉(zhuǎn)載于:https://www.cnblogs.com/mengfanrong/p/4198664.html
總結(jié)
以上是生活随笔為你收集整理的内存泄漏以及常见的解决方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Java】RuleSource约束常用
- 下一篇: 《BI那点儿事》三国数据分析系列——蜀汉