• <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>
            隨筆-91  評(píng)論-137  文章-0  trackbacks-0
            上一篇中我們已經(jīng)實(shí)現(xiàn)了一個(gè)簡單的內(nèi)存池,可以申請更大塊的內(nèi)存塊來減少申請小塊內(nèi)存塊時(shí)產(chǎn)生的內(nèi)存碎片。

            在本篇中,我們需要為其加入內(nèi)存泄漏的檢測代碼,以此來檢測代碼編寫過程中的疏忽帶來的內(nèi)存泄漏。(callstack的顯示暫時(shí)僅支持Windows)

            一、內(nèi)存泄漏檢測
            首先,改寫obj和block結(jié)構(gòu),在obj中加入一個(gè)域released表示這個(gè)chunk是否被釋放
             1     struct obj
             2     {
             3 #ifdef _DEBUG
             4         bool      released;
             5 
             6 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) // Only windows can get callstack
             7 #define CALLSTACK_MAX_DEPTH 30
             8         UINT_PTR  callStack[CALLSTACK_MAX_DEPTH];
             9         DWORD     dwCallStackDepth; // Real depth
            10 #endif
            11 
            12 #endif
            13         obj*      next;
            14     };
            15 
            16     struct block
            17     {
            18         block*    next;
            19         void*     data;
            20 #ifdef _DEBUG
            21         size_type size;
            22 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
            23         UINT_PTR  callStack[CALLSTACK_MAX_DEPTH];
            24         DWORD     dwCallStackDepth;
            25 #endif
            26 #endif
            27     };
            其中的callstack部分將在下一節(jié)中介紹

            然后,我們增加一個(gè)結(jié)構(gòu)
            1 #ifdef _DEBUG
            2     struct use
            3     {
            4         obj* data;
            5         use* next;
            6     };
            7 #endif
            其中data域指向了一塊分配出去的小內(nèi)存塊,next域形成了一張鏈表。

            然后,我們添加一個(gè)成員變量來保存這張鏈表,以及一個(gè)函數(shù)來將一個(gè)chunk插入這張鏈表
            #ifdef _DEBUG
                use*      use_list;
            #endif

            #ifdef _DEBUG
            inline void MemoryPool::addUseInfo(obj* ptr)
            {
                use* p = (use*)malloc(sizeof(use));
                p->data = ptr;
                p->next = use_list;
                use_list = p;
            }
            #endif

            然后,我們來改寫refill函數(shù)使其在分配內(nèi)存塊時(shí)打上released標(biāo)記,并將每個(gè)分配的內(nèi)存塊記錄下來
             1 void* MemoryPool::refill(int i, void(*h)(size_type))
             2 {
             3     const int count = 20;
             4     const int preSize = (i + 1) * ALIGN + headerSize;
             5     char* p = (char*)malloc(preSize * count);
             6     while(p == 0)
             7     {
             8         h(preSize * count);
             9         p = (char*)malloc(preSize * count);
            10     }
            11     block* pBlock = (block*)malloc(sizeof(block));
            12     while(pBlock == 0)
            13     {
            14         h(sizeof(block));
            15         pBlock = (block*)malloc(sizeof(block));
            16     }
            17     pBlock->data = p;
            18     pBlock->next = free_list;
            19     free_list = pBlock;
            20 
            21     obj* current = (obj*)p;
            22 #ifdef _DEBUG
            23     addUseInfo(current);
            24     current->released = false;
            25 #endif
            26     current = (obj*)((char*)current + preSize);
            27     for(int j = 0; j < count - 1; ++j)
            28     {
            29 #ifdef _DEBUG
            30         addUseInfo(current);
            31         current->released = true;
            32 #endif
            33         current->next = chunk_list[i];
            34         chunk_list[i] = current;
            35         current = (obj*)((char*)current + preSize);
            36     }
            37     return (char*)p + headerSize;
            38 }
            其中的headerSize跟callstack有關(guān),將在下一節(jié)中介紹。

            當(dāng)然,在deallocate時(shí)要將此內(nèi)存塊的released標(biāo)記打?yàn)閠rue
             1 void MemoryPool::deallocate(void* p, size_type n)
             2 {
             3     if(p == 0) return;
             4     if(n > MAX_BYTES)
             5     {
             6         free(p);
             7         return;
             8     }
             9     const int i = INDEX(ROUND_UP(n));
            10 #ifdef _DEBUG
            11     p = (char*)p - (int)headerSize;
            12     obj* ptr = reinterpret_cast<obj*>(p);
            13     if (ptr->released) throw error<char*>("chunk has already released", __FILE__, __LINE__);
            14     ptr->released = true;
            15 #endif
            16     reinterpret_cast<obj*>(p)->next = chunk_list[i];
            17     chunk_list[i] = reinterpret_cast<obj*>(p);
            18 }

            OK,現(xiàn)在已經(jīng)有模有樣了,可以松口氣了。接下來是最重要的部分,在MemoryPool析構(gòu)時(shí)檢測這個(gè)Pool內(nèi)的use_list中是否有chunk的released標(biāo)記為true(內(nèi)存泄漏了)
             1 MemoryPool::~MemoryPool()
             2 {
             3 #ifdef _DEBUG
             4     while (use_list)
             5     {
             6         use *ptr = use_list, *next = use_list->next;
             7         if (!ptr->data->released)
             8         {
             9             obj* pObj = ptr->data;
            10             Console::SetColor(truefalsefalsetrue);
            11             throw error<char*>("chunk leaked", __FILE__, __LINE__);
            12         }
            13         free(ptr);
            14         use_list = next;
            15     }
            16 #endif
            17     clear();
            18 }
            其實(shí)說來也容易,只需要檢測每個(gè)chunk的released標(biāo)記是否為true就行了,而最后的clear函數(shù)是以前析構(gòu)函數(shù)的代碼,用來釋放所有申請的block和大塊的chunk。

            OK,現(xiàn)在我們已經(jīng)可以檢測出沒有被deallocate的chunk了。

            二、callstack
            首先,我們先來看一個(gè)Windows API,“CaptureStackBackTrace”這個(gè)API通過傳入的一個(gè)數(shù)組來得到一組地址。當(dāng)然有這個(gè)API并不夠,我們還需要知道是哪個(gè)文件的第幾行。“SymGetSymFromAddr64”這個(gè)API用來獲取某個(gè)地址對(duì)應(yīng)的函數(shù)名,“SymGetLineFromAddr64”這個(gè)API則是用來獲取某個(gè)地址對(duì)應(yīng)的文件名和行號(hào)的,這兩個(gè)函數(shù)的32位版本則是不帶64的。有了這些Windows API,我們就可以很輕松的獲取到當(dāng)前函數(shù)的調(diào)用堆棧了,主要的功勞還是要?dú)w功于Windows強(qiáng)大的dbghelp。

            最后,完整的代碼你可以在http://code.google.com/p/qlanguage/中找到。
            posted on 2013-01-19 20:09 lwch 閱讀(1944) 評(píng)論(0)  編輯 收藏 引用 所屬分類: STL
            久久99国产精品尤物| 青青热久久国产久精品| 亚洲国产精品18久久久久久| 欧美噜噜久久久XXX| 亚洲综合精品香蕉久久网97| 欧美日韩中文字幕久久久不卡| 久久夜色精品国产| 久久99精品久久久久久久久久 | 国内精品久久国产大陆| 亚洲欧美日韩精品久久| 一本大道久久东京热无码AV| 久久国产色AV免费观看| 久久婷婷五月综合色奶水99啪| 久久精品aⅴ无码中文字字幕不卡| 国产亚洲成人久久| 久久婷婷五月综合色高清| 国产成人精品久久亚洲高清不卡| 精品久久久久成人码免费动漫 | 亚洲国产精品无码久久久蜜芽| 亚洲天堂久久精品| 99久久精品费精品国产一区二区| 久久精品国产亚洲av瑜伽| 久久精品国产免费一区| 久久久久久久人妻无码中文字幕爆 | 午夜精品久久影院蜜桃| 狠狠久久综合伊人不卡| 精品久久无码中文字幕| 国产成人久久精品一区二区三区| 国产精品亚洲综合专区片高清久久久| 囯产极品美女高潮无套久久久 | 亚洲香蕉网久久综合影视| 日韩十八禁一区二区久久| 久久久久国产精品三级网| 18岁日韩内射颜射午夜久久成人| 国内精品伊人久久久久av一坑| 性做久久久久久久| 午夜精品久久久久久久| 久久99亚洲网美利坚合众国| 狼狼综合久久久久综合网| 精品久久久无码人妻中文字幕| 综合久久精品色|