内存泄漏以及常见的解决方法
? 之所以撰寫這篇文章是由于前段時間花費了非常大的精力在已經成熟的代碼上再去處理memory leak問題。寫此的目的是希望我們應該養成良好的編碼習慣,盡可能的避免這種問題,由于當你對著一大片的代碼再去處理此類的問題,此時無疑添加了解決的成本和難度。準確的說屬于補救措施了。 1. 什么是內存泄漏(memory leak)?
?指因為疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏并不是指內存在物理上的消失,而是應用程序分配某段內存后,因為設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。?
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. 對于C和C++這樣的沒有Garbage Collection 的語言來講,我們主要關注兩種類型的內存泄漏:
?? 堆內存泄漏(Heap leak)。對內存指的是程序執行中依據須要分配通過malloc,realloc new等從堆中分配的一塊內存,再是完畢后必須通過調用相應的 free或者delete 刪掉。假設程序的設計的錯誤導致這部分內存沒有被釋放,那么此后這塊內存將不會被使用,就會產生Heap Leak.?
? 系統資源泄露(Resource Leak).主要指程序使用系統分配的資源比方 Bitmap,handle ,SOCKET等沒有使用對應的函數釋放掉,導致系統資源的浪費,嚴重可導致系統效能減少,系統執行不穩定。??
3. 怎樣解決內存泄露?
內存泄露的問題其困難在于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)資源獲取即初始化,它是一項非常easy的技術,利用C++對象生命周期的概念來控制程序的資源,比如內存,文件句柄,網絡連接以及審計追蹤(audit trail)等.RAII的基本技術原理非常easy.若希望保持對某個重要資源的跟蹤,那么創建一個對象,并將資源的生命周期和對象的生命周期相關聯.如此一來,就能夠利用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<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)
?{
轉載于:https://www.cnblogs.com/mengfanrong/p/4198664.html
總結
以上是生活随笔為你收集整理的内存泄漏以及常见的解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Java】RuleSource约束常用
- 下一篇: 《BI那点儿事》三国数据分析系列——蜀汉