• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            面對(duì)現(xiàn)實(shí),超越自己
            逆水行舟,不進(jìn)則退
            posts - 269,comments - 32,trackbacks - 0
            之所以撰寫(xiě)這篇文章是因?yàn)榍岸螘r(shí)間花費(fèi)了很大的精力在已經(jīng)成熟的代碼上再去處理memory leak問(wèn)題。寫(xiě)此的目的是希望我們應(yīng)該養(yǎng)成良好的編碼習(xí)慣,盡可能的避免這樣的問(wèn)題,因?yàn)楫?dāng)你對(duì)著一大片的代碼再去處理此類的問(wèn)題,此時(shí)無(wú)疑增加了解決的成本和難度。準(zhǔn)確的說(shuō)屬于補(bǔ)救措施了。
            1. 什么是內(nèi)存泄漏(memory leak)?

             指由于疏忽或錯(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并非指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,由于設(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)注兩種類型的內(nèi)存泄漏:

               堆內(nèi)存泄漏(Heap leak)。對(duì)內(nèi)存指的是程序運(yùn)行中根據(jù)需要分配通過(guò)malloc,realloc new等從堆中分配的一塊內(nèi)存,再是完成后必須通過(guò)調(diào)用對(duì)應(yīng)的 free或者delete 刪掉。如果程序的設(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)有使用相應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費(fèi),嚴(yán)重可導(dǎo)致系統(tǒng)效能降低,系統(tǒng)運(yùn)行不穩(wěn)定。  

            3. 如何解決內(nèi)存泄露?

            內(nèi)存泄露的問(wèn)題其困難在于1.編譯器不能發(fā)現(xiàn)這些問(wèn)題。2.運(yùn)行時(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)建字體的例子:
            示例代碼
            如果使用完成時(shí)候忘記釋放字體,就造成了資源泄漏。 

               對(duì)于基于引用計(jì)數(shù)的系統(tǒng)對(duì)象尤其要注意,因?yàn)橹挥衅湟糜?jì)數(shù)為0時(shí),該對(duì)象才能正確被刪除。而其使用過(guò)程中有其生成的新的系統(tǒng)資源,使用完畢后,如果沒(méi)有及時(shí)刪除,都會(huì)影響其引用計(jì)數(shù)。
            示例代碼

            DNS_Release(pMe->m_pDns);//當(dāng)程序運(yùn)行到此時(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)很簡(jiǎn)單的技術(shù),利用C++對(duì)象生命周期的概念來(lái)控制程序的資源,例如內(nèi)存,文件句柄,網(wǎng)絡(luò)連接以及審計(jì)追蹤(audit trail)等.RAII的基本技術(shù)原理很簡(jiǎn)單.若希望保持對(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ō)明。
            示例代碼

            其主要思路是將分配的內(nèi)存以鏈表的形式自行管理,使用完畢之后從鏈表中刪除,程序結(jié)束時(shí)可檢查改鏈表,其中記錄了內(nèi)存泄露的文件,所在文件的行數(shù)以及泄露的大小哦。
            第三,Boost 中的smart pointer(待完善,結(jié)合大家的建議)
            第四,一些常見(jiàn)的工具插件,詳見(jiàn)我的Blog中相關(guān)文章。

            4. 由內(nèi)存泄露引出內(nèi)存溢出話題:

            所謂內(nèi)存溢出就是你要求分配的內(nèi)存超出了系統(tǒng)能給你的,系統(tǒng)不能滿足需求,于是會(huì)產(chǎn)生內(nèi)存溢出的問(wèn)題。

            常見(jiàn)的溢出主要有:

            內(nèi)存分配未成功,卻使用了它。 常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL。如果指針p 是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進(jìn)行檢查。如果是用malloc 或new 來(lái)申請(qǐng)內(nèi)存,應(yīng)該用if(p==NULL)或if(p!=NULL)進(jìn)行防錯(cuò)處理。

            內(nèi)存分配雖然成功,但是尚未初始化就引用它。 內(nèi)存分配成功并且已經(jīng)初始化,但操作越過(guò)了內(nèi)存的邊界。 例如在使用數(shù)組時(shí)經(jīng)常發(fā)生下標(biāo)“多1”或者“少1”的操作。特別是在for 循環(huán)語(yǔ)句中,循環(huán)次數(shù)很容易搞錯(cuò),導(dǎo)致數(shù)組操作越界。

            使用free 或delete 釋放了內(nèi)存后,沒(méi)有將指針設(shè)置為NULL。導(dǎo)致產(chǎn)生“野指針”。

            程序中的對(duì)象調(diào)用關(guān)系過(guò)于復(fù)雜,實(shí)在難以搞清楚某個(gè)對(duì)象究竟是否已經(jīng)釋放了內(nèi)存,此時(shí)應(yīng)該重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),從根本上解決對(duì)象管理的混亂局面。(這點(diǎn)可是深有感受,呵呵)

             不要忘記為數(shù)組和動(dòng)態(tài)內(nèi)存賦初值。防止將未被初始化的內(nèi)存作為右值使用。

            windows如何下防止內(nèi)存泄露

            windows下開(kāi)發(fā)C++程序的時(shí)候,我們經(jīng)常需要用到malloc開(kāi)申請(qǐng)內(nèi)存,然后利用free回收內(nèi)存,但是開(kāi)發(fā)人員的不小心可能會(huì)忘記free掉內(nèi)存,這樣就導(dǎo)致了內(nèi)存泄露

            利用庫(kù)檢測(cè)內(nèi)存泄露信息

            #define _CRTDBG_MAP_ALLOC  //如果沒(méi)有這個(gè)宏定義,我們只能知道有內(nèi)存泄露,卻無(wú)法知道在哪個(gè)地方申請(qǐng)內(nèi)存忘記了釋放

            #include
            <stdlib.h>
            #include
            <crtdbg.h>
            int main(void)
            {
                
            char *= (char *)malloc(sizeof(char* 100);
                _CrtDumpMemoryLeaks();
            }
            使用crtdbg來(lái)檢測(cè)到內(nèi)存泄露很簡(jiǎn)單,只要在文件的第一行定義_CRTDBG_MAP_ALLOC,然后include頭文件crtdbg.h,在程序需要內(nèi)存檢測(cè)的地方調(diào)用_CrtDumpMemoryLeaks,就可以輸出內(nèi)存泄露的信息,如上面的程序,我們申請(qǐng)了100個(gè)字節(jié)的內(nèi)存而沒(méi)有釋放,但是我們可以很清楚地看到內(nèi)存泄露在 哪個(gè)地方。

             

            我們?cè)?/span>main.cpp這個(gè)文件中的第八行申請(qǐng)了內(nèi)存,但是沒(méi)有進(jìn)行釋放

            那么編譯器是怎么知道我們有內(nèi)存泄露呢??就是利用宏定義把我們的調(diào)用的malloc替換成crtdbg 庫(kù)里面的_malloc_dbg,我們?cè)谏暾?qǐng)內(nèi)存的時(shí)候,_malloc_dbg會(huì)先記錄下我們申請(qǐng)內(nèi)存的行數(shù)以及大小(記得編譯器有內(nèi)置的宏定義__LINE____FILE__?),把這些信息放到一個(gè)list(只是舉例,使用list保存這些信息一旦程序大了會(huì)很慢)里面,當(dāng)我們free內(nèi)存的時(shí)候,把這塊內(nèi)存的信息從list里面刪除掉,我們調(diào)用_CrtDumpMemoryLeaks()的時(shí)候就是在把這個(gè)list的信息依次打印出來(lái)而已

            下面是我們定義_CRTDBG_MAP_ALLOC后實(shí)際上所調(diào)用的malloc原型,malloc已經(jīng)成了一個(gè)宏定義

            #define   malloc(s)             _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)

            當(dāng)然,我們一般調(diào)用_CrtDumpMemoryLeaks的時(shí)候都是在程序的結(jié)尾處,如果我們的程序有多個(gè)出口,我們值需要在程序開(kāi)始處調(diào)用_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ) 就可以了

            有時(shí)候我們需要檢測(cè)某一段代碼是否有內(nèi)存泄露,crtdbg庫(kù)也可以幫我們做到

            _CrtMemState s1;
            _CrtMemState s2;  
            _CrtMemCheckpoint(
            &s1); 
            char *p2 = (char *)malloc(400);
            _CrtMemCheckpoint(
            &s2);
            _CrtMemState s3;
            if (_CrtMemDifference(&s3,&s1,&s2))
            {
                 _CrtMemDumpStatistics(
            &s3);
            }

             

            這樣,我們?cè)谳敵龃翱趯?huì)看到s1s2之間的內(nèi)存使用信息:

            0 bytes in 0 Free Blocks. 400 bytes in 1 Normal Blocks. 0 bytes in 0 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 0 bytes. Total allocations: 400 bytes.

            crtdbg庫(kù)也有缺點(diǎn),當(dāng)你使用一個(gè)別人提供的lib或者dll庫(kù)的時(shí)候,你調(diào)用這個(gè)函數(shù),這個(gè)函數(shù)分配了內(nèi)存,需要你去調(diào)用另外一個(gè)函數(shù)才能把內(nèi)存釋放掉,但是你不知道這個(gè)函數(shù)需要調(diào)用另外一個(gè)函數(shù)才能釋放掉內(nèi)存,這個(gè)是無(wú)法通過(guò)crtdbg庫(kù)檢測(cè)出來(lái)的,這個(gè)函數(shù)包括c++new函數(shù),所以這個(gè)庫(kù)實(shí)際上不適用C++

            利用share_ptr來(lái)管理內(nèi)存

            如果有使用過(guò)boost庫(kù)的應(yīng)該知道,boost里面有一個(gè)shart_ptr被譽(yù)為神器,因?yàn)樗梢詭臀覀冏詣?dòng)管理內(nèi)存,具體用法很簡(jiǎn)單:

              boost::shared_ptr < connection > p ( new connection());

            這樣的話我們不需要去delete內(nèi)存,shartd_ptr會(huì)在我們不需要這快內(nèi)存的時(shí)候幫我們delete掉,shartd_ptr內(nèi)部是使用引用計(jì)數(shù)以及C++RAII,有別的對(duì)象引用該指針的時(shí)候引用技術(shù)就+1shartd_ptr析構(gòu)函數(shù)調(diào)用的時(shí)候引用計(jì)數(shù)就-1,當(dāng)為0的時(shí)候就delete掉該指針,所以我們并不需要調(diào)用delete來(lái)釋放資源,share_ptr會(huì)幫我們管理

            shared_ptr雖然看起來(lái)很好用,但是當(dāng)程序一旦復(fù)雜起來(lái),shared_ptr依然也會(huì)變復(fù)雜(shared_ptr四宗罪),當(dāng)然boost本身就比較復(fù)雜,這個(gè)也是我比較不喜歡boost的一個(gè)原因

            將資源集中管理

            這個(gè)也是我比較經(jīng)常使用的方法,特別是在大程序的使用,配合單件模式,將資源在整個(gè)程序或者模塊中集中管理,這樣在程序結(jié)束的時(shí)候只要我們?cè)谖鰳?gòu)函數(shù)里面有清理這些資源,我們就可以避免內(nèi)存泄露,對(duì)于數(shù)據(jù)的一些寫(xiě)操作全部在這個(gè)類中統(tǒng)一操作,如果要暴露內(nèi)部的數(shù)據(jù),只對(duì)外提供const數(shù)據(jù)(可以通過(guò)強(qiáng)轉(zhuǎn)去掉const屬性)

            當(dāng)然這個(gè)方法并不適用于所有場(chǎng)景,比如我們需要提供庫(kù)給別人,我們沒(méi)辦法預(yù)測(cè)到客戶需要什么操作,所以這個(gè)方法只適用內(nèi)部團(tuán)隊(duì)開(kāi)發(fā)

            總之內(nèi)存管理?yè)?jù)我所知到現(xiàn)在還是沒(méi)有什么好的解決方法,特別是當(dāng)代碼一旦膨脹的時(shí)候,到現(xiàn)在好像javapythonerlang都有內(nèi)存泄露的問(wèn)題,我們只能在平常開(kāi)發(fā)中多注意了

            參考資料:

            陳碩的博客(有一些shared_ptr的資料,也可以從這里看出shared_ptr使用起來(lái)沒(méi)那么簡(jiǎn)單)
            shared_ptr四宗罪
            MSDN crtdbg庫(kù)

            本文相關(guān)鏈接:http://blog.csdn.net/na_he/article/details/7429171
                            http://www.cnblogs.com/linyilong3/archive/2013/03/23/2977247.html

            posted on 2013-05-02 18:29 王海光 閱讀(7177) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C++
            久久亚洲精品中文字幕三区| 久久国产三级无码一区二区| 久久亚洲国产最新网站| 久久99精品久久久久久9蜜桃| 国内精品久久久久久久涩爱| 精品久久久久久无码不卡| 久久九九精品99国产精品| 中文精品久久久久国产网址| 伊人久久大香线蕉成人| 国产精品久久一区二区三区| 思思久久精品在热线热| 品成人欧美大片久久国产欧美...| 狠狠色丁香婷婷久久综合五月 | 亚洲综合久久夜AV | 精品乱码久久久久久久| 性做久久久久久久久| 欧美精品一本久久男人的天堂| 国产精品久久婷婷六月丁香| 国产成人无码精品久久久免费| 性高湖久久久久久久久| 亚洲人成无码www久久久| 成人亚洲欧美久久久久| 国产一级持黄大片99久久| 99精品国产99久久久久久97| 亚洲精品无码久久久久AV麻豆| 草草久久久无码国产专区| 久久se精品一区精品二区| 久久人人爽人人爽人人AV东京热| 久久人人爽人人人人片av| 久久久受www免费人成| 99久久综合狠狠综合久久| 99久久伊人精品综合观看| 天天综合久久久网| 91精品国产综合久久香蕉| 亚洲一区二区三区日本久久九| AV色综合久久天堂AV色综合在| 亚洲中文字幕无码久久精品1 | 久久亚洲精品人成综合网| 狠狠综合久久AV一区二区三区| 精品久久亚洲中文无码| 欧美一区二区三区久久综合|