1. 程式人生 > >如何減少頻繁分配記憶體(malloc或者new)造成的記憶體碎片

如何減少頻繁分配記憶體(malloc或者new)造成的記憶體碎片

高效能之記憶體池(頻繁使用malloc和new會降低效能)
記憶體池(Memory Pool)是一種記憶體分配方式。通常我們習慣直接使用new、malloc等API申請分配記憶體,這樣做的缺點在於:由於所申請記憶體塊的大小不定,當頻繁使用時會造成大量的記憶體碎片並進而降低效能。記憶體池則是在真正使用記憶體之前,先申請分配一定數量的、大小相等(一般情況下)的記憶體塊留作備用。當有新的記憶體需求時,就從記憶體池中分出一部分記憶體塊,若記憶體塊不夠再繼續申請新的記憶體。這樣做的一個顯著優點是儘量避免了記憶體碎片,使得記憶體分配效率得到提升。

(1)針對特殊情況,例如需要頻繁分配釋放固定大小的記憶體物件時,不需要複雜的分配演算法和多執行緒保護。也不需要維護記憶體空閒表的額外開銷,從而獲得較高的效能。


(2)由於開闢一定數量的連續記憶體空間作為記憶體池塊,因而一定程度上提高了程式區域性性,提升了程式效能。
(3)比較容易控制頁邊界對齊和記憶體位元組對齊,沒有記憶體碎片的問題。
(4)當需要分配管理的記憶體在100M一下的時候,採用記憶體池會節省大量的時間,否則會耗費更多的時間。
(5)記憶體池可以防止更多的記憶體碎片的產生

(6)更方便於管理記憶體

利用C/C++開發大型應用程式中,記憶體的管理與分配是一個需要認真考慮的部分。

本文描述了記憶體池設計原理並給出記憶體池的實現程式碼,程式碼支援Windows和Linux,多執行緒安全。

記憶體池設計過程中需要考慮好記憶體的分配與釋放問題,其實也就是空間和時間的矛盾。

有的記憶體池設計得很巧妙,記憶體分配與需求相當,但是會浪費過多的時間去查詢分配與釋放,這就得不償失;

實際使用中,我們更多的是關心記憶體分配的速度,而不是記憶體的使用效率。基於此,本文按照如下思想設計實現記憶體池。

主要包含三個結構:StiaticMemory, MemoryChunk和MemoryBlock,三者之間的關係如下圖所示:

1.記憶體的分配:

(1)如果分配大小超過1024,直接採用malloc分配,分配的時候多分配sizeof(size_t)位元組,用於儲存該塊的大小;

(2)否則根據分配大小,查詢到容納該大小的最小size的MemoryChunk;

(3)查詢MemoryChunk的連結串列指標pList,找到空閒的MemoryBlock返回;

(4)如果pList為NULL,臨時建立MemoryBlock返回;

(5)MemoryBlock頭部包含兩個成員,pChunk指向的所屬的MemoryChunk物件,size表明大小,其後才是給使用者使用的空間;

2.記憶體的釋放:

(1)根據釋放的指標,查詢器size頭部,即減去sizeof(size_t)位元組,判斷該塊的大小;

(2)如果大小超過1024,直接free;

(3)否則交給MemoryChunk處理,而塊的頭部儲存了該指標,因此直接利用該指標就可以收回該記憶體。

注意的問題:

上述設計的記憶體池通過冗餘的頭部來實現記憶體塊的分配與釋放,減少了記憶體池的操作時間,速度上要優於原始的malloc和free操作,同時減少了記憶體碎片的增加。

但是該設計中沒有去驗證釋放的塊冗餘頭部的正確性,因此故意釋放不屬於記憶體池中的塊或者修改頭部資訊都會導致記憶體池操作失敗,當然這些可以由程式設計師來控制。

此外,記憶體池中分配出去的記憶體塊如果不主動釋放,記憶體池沒有保留資訊,不會自動釋放,但是在退出的時候會驗證驗證是否完全釋放,其實這個在系統測試時候就可以檢測出來,我想這個缺陷也是可以彌補的,在此提出,希望使用者注意。

下面貼上原始碼,如果對程式碼有任何建議或者發現存在的Bug,希望與我聯絡,共同學習交流,Tx。

MemoryChunk.h 檔案,執行緒安全

  1. #ifndef MEMORY_CHUNK_H  
  2. #define MEMORY_CHUNK_H  
  3. #include <cstdio>  
  4. #include <cassert>  
  5. #include <cstdlib>  
  6. #ifdef WIN32  
  7. #include <windows.h>  
  8. typedef CRITICAL_SECTION MUTEXTYPE;  
  9. #define INITMUTEX(hMutex) InitializeCriticalSection(&hMutex)  
  10. #define DELMUTEX(hMutex) DeleteCriticalSection(&hMutex)  
  11. #define LOCK(hMutex) EnterCriticalSection(&hMutex)  
  12. #define UNLOCK(hMutex) LeaveCriticalSection(&hMutex)  
  13. #else  
  14. #include <pthread.h>  
  15. typedef pthread_mutex_t MUTEXTYPE;  
  16. #define INITMUTEX(hMutex) pthread_mutex_init(&hMutex,NULL)  
  17. #define DELMUTEX(hMutex) pthread_mutex_destroy(&hMutex)  
  18. #define LOCK(hMutex) pthread_mutex_lock(&hMutex)  
  19. #define UNLOCK(hMutex) pthread_mutex_unlock(&hMutex)  
  20. #endif  
  21. class MemoryChunk;  
  22. /** @struct MemoryBlock  
  23.  *  
  24.  */  
  25. struct BlockHeader  
  26. {  
  27.     MemoryChunk* pChunk;  
  28.     size_t len;  
  29. };  
  30. struct MemoryBlock;  
  31. struct BlockData  
  32. {  
  33.     union{  
  34.         MemoryBlock* pNext;  
  35.         char pBuffer;  
  36.     };  
  37. };  
  38. struct MemoryBlock  
  39. {  
  40.     BlockHeader header;  
  41.     BlockData data;  
  42. };  
  43. /** @class MemoryChunk  
  44.  *  
  45.  */  
  46. class MemoryChunk  
  47. {  
  48. public:  
  49.     MemoryChunk(size_t size, int count)  
  50.     {  
  51.         INITMUTEX(hMutex);  
  52.         this->pFreeList=NULL;  
  53.         this->size=size;  
  54.         this->count=0;  
  55.         MemoryBlock* pBlock;  
  56.         while(count--){  
  57.             pBlock=CreateBlock();  
  58.             if(!pBlock)break;  
  59.             pBlock->data.pNext=pFreeList;  
  60.             pFreeList=pBlock;  
  61.         }  
  62.     }  
  63.     ~MemoryChunk()  
  64.     {  
  65.         int tempcount=0;  
  66.         MemoryBlock* pBlock;  
  67.         while(pBlock=pFreeList){  
  68.             pFreeList=pBlock->data.pNext;  
  69.             DeleteBlock(pBlock);  
  70.             ++tempcount;  
  71.         }  
  72.         assert(tempcount==count);//!確保釋放完全  
  73.         DELMUTEX(hMutex);  
  74.     }  
  75.     void* malloc()  
  76.     {  
  77.         MemoryBlock* pBlock;  
  78.         LOCK(hMutex);  
  79.         if(pFreeList){  
  80.             pBlock=pFreeList;  
  81.             pFreeList=pBlock->data.pNext;  
  82.         }  
  83.         else{  
  84.             if(!(pBlock=CreateBlock())){  
  85.                 UNLOCK(hMutex);  
  86.                 return NULL;  
  87.             }  
  88.         }  
  89.         UNLOCK(hMutex);  
  90.         return &pBlock->data.pBuffer;  
  91.     }  
  92.     static void free(void* pMem)  
  93.     {  
  94.         MemoryBlock* pBlock=(MemoryBlock*)((char*)pMem-sizeof(BlockHeader));  
  95.         pBlock->header.pChunk->free(pBlock);  
  96.     }  
  97.     void free(MemoryBlock* pBlock)  
  98.     {  
  99.         LOCK(hMutex);  
  100.         pBlock->data.pNext=pFreeList;  
  101.         pFreeList=pBlock;  
  102.         UNLOCK(hMutex);  
  103.     }  
  104.     MemoryChunk* Next(){return pNext;}  
  105. protected:  
  106.     MemoryBlock* CreateBlock()  
  107.     {  
  108.         MemoryBlock* pBlock=(MemoryBlock*)::malloc(sizeof(BlockHeader)+size);  
  109.         if(pBlock){  
  110.             pBlock->header.pChunk=this;  
  111.             pBlock->header.len=size;  
  112.             ++count;  
  113.         }  
  114.         return pBlock;  
  115.     }  
  116.     void DeleteBlock(MemoryBlock* pBlock)  
  117.     {  
  118.         ::free(pBlock);  
  119.     }  
  120. private:  
  121.     MemoryBlock* pFreeList;  
  122.     size_t size;//!Block大小  
  123.     int count;//!Block數目  
  124.     MemoryChunk* pNext;  
  125.     MUTEXTYPE hMutex;  
  126. };  
  127. #endif  

StaticMemory.h檔案,記憶體池物件

  1. #ifndef STATIC_MEMORY_H  
  2. #define STATIC_MEMORY_H  
  3. #include "MemoryChunk.h"  
  4. /** @ StaticMemory.h  
  5.  * 定義實現記憶體池  
  6.  * 採用固定大小策略進行記憶體管理與分配  
  7.  * 減少因大量小記憶體分配導致的記憶體碎片增加  
  8.  */  
  9. struct HeapHeader  
  10. {  
  11.     size_t size;  
  12. };  
  13. struct MemoryHeap  
  14. {  
  15.     HeapHeader header;  
  16.     char pBuffer;  
  17. };  
  18. class StaticMemory  
  19. {  
  20. public:  
  21.     typedef enum{MAX_SIZE=1024,MIN_SIZE=sizeof(MemoryChunk*)};  
  22.     StaticMemory()  
  23.     {  
  24.         chunkcount=0;  
  25.         for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)++chunkcount;  
  26.         //pChunkList=(MemoryChunk**)malloc(sizeof(MemoryChunk*)*chunkcount);  
  27.         pChunkList=new MemoryChunk*[chunkcount];  
  28.         int index=0;  
  29.         for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)  
  30.         {  
  31.             pChunkList[index++]=new MemoryChunk(size,1000);  
  32.         }  
  33.     }  
  34.     ~StaticMemory()  
  35.     {  
  36.         for(int index=0; index<chunkcount; ++index)  
  37.         {  
  38.             delete pChunkList[index];  
  39.         }  
  40.         //free(pChunkList);  
  41.         delete[] pChunkList;  
  42.     }  
  43.     void* Malloc(size_t size)  
  44.     {  
  45.         if(size>MAX_SIZE){  
  46.             return malloc(size);  
  47.         }  
  48.         int index=0;  
  49.         for(size_t tsize=MIN_SIZE; tsize<=MAX_SIZE; tsize*=2){  
  50.             if(tsize>=size)break;  
  51.             ++index;  
  52.         }  
  53.         return pChunkList[index]->malloc();  
  54.     }  
  55.     void Free(void* pMem)  
  56.     {  
  57.         if(!free(pMem))MemoryChunk::free(pMem);  
  58.     }  
  59. protected:  
  60.     void* malloc(size_t size)  
  61.     {  
  62.         MemoryHeap* pHeap=(MemoryHeap*)::malloc(sizeof(HeapHeader)+size);  
  63.         if(pHeap){  
  64.             pHeap->header.size=size;  
  65.             return &pHeap->pBuffer;  
  66.         }  
  67.         return NULL;  
  68.     }  
  69.     bool free(void* pMem)  
  70.     {  
  71.         MemoryHeap* pHeap=(MemoryHeap*)((char*)pMem-sizeof(HeapHeader));  
  72.         if(pHeap->header.size>MAX_SIZE){  
  73.             ::free(pHeap);  
  74.             return true;  
  75.         }  
  76.         return false;  
  77.     }  
  78. private:  
  79.     MemoryChunk** pChunkList;  
  80.     int chunkcount;  
  81. };  
  82. #endif  

ObejctManager.h檔案,用於實現物件的建立與管理,比較簡易。

  1. #ifndef OBJECT_MANAGER_H  
  2. #define OBJECT_MANAGER_H  
  3. #include "StaticMemory.h"  
  4. /** @class ObjectManager  
  5.  * 實現利用記憶體池建立物件  
  6.  * 要求物件具有預設建構函式  
  7.  */  
  8. template<typename T>  
  9. class ObjectManager  
  10. {  
  11. public:  
  12.     typedef T ObjectType;  
  13.     static ObjectType* Create(StaticMemory* pool)  
  14.     {  
  15.         void* pobject=pool->Malloc(sizeof(T));  
  16.         new(pobject) ObjectType();  
  17.         return static_cast<ObjectType*>(pobject);  
  18.     }  
  19.     static void Delete(StaticMemory* pool, ObjectType* pobject)  
  20.     {  
  21.         pobject->~ObjectType();  
  22.         pool->Free(pobject);  
  23.     }  
  24. };  
  25. #endif  

測試結果:

分單執行緒和多執行緒進行測試,重複的記憶體分配與釋放在實際使用中是不太可能的,為了模擬實際使用,通過隨機數來確定分配記憶體大小,同時也通過隨機數來確定分配與釋放操作。在測試過程中限制最大分配大小為1024,目的是為了測試小記憶體塊的分配情況對比。

記憶體池單執行緒測試結果
分配與釋放次數 malloc/free 記憶體池
                                                        100,000             0.01s         0.01s
                                                      1,000,000             0.15s         0.11s
                                                     10,000,000             1.26s         0.60s
                                                    100,000,000             9.21s         5.99s
                                                  1,000,000,000             92.70s         61.46s
記憶體池多執行緒測試結果
   執行緒數目                 malloc/free                       記憶體池
1/1,000,000                   0.15s                       0.10s
2/1,000,000                  1.49s                       0.73s
4/1,000,000                  9.945s                       6.71s
8/1,000,000                  45.60s                      28.82s

進行多執行緒測試主要是測試多執行緒執行下,加鎖給記憶體分配帶來的影響,因此為了排除CPU的影響,測試採用的機器為16盒,16G記憶體的Linux伺服器。

具體配置如下:

Intel(R) Xeon(R) CPU           E5630  @ 2.53GHz

stepping        : 2
cpu MHz         : 2527.084

cache size      : 12288 KB

相關推薦

如何減少頻繁分配記憶體malloc或者new造成記憶體碎片

高效能之記憶體池(頻繁使用malloc和new會降低效能)記憶體池(Memory Pool)是一種記憶體分配方式。通常我們習慣直接使用new、malloc等API申請分配記憶體,這樣做的缺點在於:由於所申請記憶體塊的大小不定,當頻繁使用時會造成大量的記憶體碎片並進而降低效

如何減少頻繁分配記憶體malloc或者new造成記憶體碎片

http://www.coder51ad.pub/article/724635.html 記憶體池(Memory Pool)是一種記憶體分配方式。 通常我們習慣直接使用new、malloc等API申請分配記憶體,這樣做的缺點在於:由於所申請記憶體塊的大小不定,當頻繁使用時

記憶體分配malloc()和free()

C語言的一個特性是接近底層,對於硬體的控制能力比其他高階動態語言要強。同時,C語言賦予程式設計師更大的自由度,更信任程式設計師。在記憶體的分配與釋放上,我們知道非靜態變數(塊作用域,無連結,自動生存期)在程式進入到變數定義所在的地方(塊或函式內)時分配記憶體,在離開塊作用域時釋放。對於靜態變數,在程式載入到記

指標和動態分配記憶體 不定長度陣列------新標準c++程式設計

背景:   陣列的長度是定義好的,在整個程式中固定不變。c++不允許定義元素個數不確定的陣列。例如: 1 2 int n; int a[n];  //這種定義是不允許的   但是在實際程式設計中,往往會出現要處理的資料數量在程式設計時無

openstack在vmware虛機環境exsi或者workstation中實例掛死

openstack 在vmware虛機環境中安裝了openstack平臺,創建實例cirros和centos,openstack並未報錯,但是在novnc裏查看cirros實例卡在grub無法進入系統,centos實例卡在install centos7之後的界面,並且報內核錯誤。 後來發現comt

hdu6188 Duizi and Shunzi 貪心或者dp

重要 pri algorithm std urn == hdu include sca 題意 有n張牌,第i張牌上的數字是a[i]。我們定義 兩張數字是一樣的牌 為對子。我們定義 三張數字連續的牌 為順子。我們想把這n張牌組成盡可能多的順子和對子。請計算並輸出能組成的

不同package 的class的成員field或者method之間的訪問

import package名稱.class名稱; 訪問靜態變數或方法: class名.變數名;class名.方法名(args); 訪問非靜態變數或方法:例項化,基於物件訪問;  Class obj=new Class(); obj.變數名  obj.方法名   在

【演算法學習】基於“平均”的隨機分配演算法貪婪,回溯,以按平均工作量隨機分配單位為例

一、背景介紹   在工作中,遇到一個需求:將 N 個單位隨機分配給 n 個人,其中每個單位有對應的工作量,分配時要儘量按工作量平均分給 n 個人,且人員的所屬單位不能包括在被分配的單位中(N >= n)。例如:有三個部門分給兩個人([A]屬於部門2和[B]屬於部門3),部門1的

對oracle例項的記憶體SGA和PGA進行調整,優化資料庫性

一、名詞解釋 (1)SGA:SystemGlobal Area是OracleInstance的基本組成部分,在例項啟動時分配;系統全域性域SGA主要由三部分構成:共享池、資料緩衝區、日誌緩衝區。 (2)共享池:Shared Pool用於快取最近被執行的SQL

聊聊高併發三十五Java記憶體模型那些事理解記憶體屏障

硬體層提供了一系列的記憶體屏障 memory barrier / memory fence(Intel的提法)來提供一致性的能力。拿X86平臺來說,有幾種主要的記憶體屏障 1. lfence,是一種Load Barrier 讀屏障 2. sfence, 是一種Store

idea快捷鍵單個或者模式的修改

將idea的快捷鍵模式改成eclipse(或其他)編輯器的模式 對於剛接觸idea的小夥伴來說,熟悉idea大量的快捷鍵是比較耗費精力的。對於這樣情況,idea提供了一種將快捷鍵的模式調成別的編輯器模式(單憑這點,idea就是碾壓的優勢)。 File-》Settings(

藍橋杯 演算法訓練 ALGO-116 最大的算式 動態規劃 資源分配型別最大乘積

演算法訓練 最大的算式 時間限制:1.0s 記憶體限制:256.0MB 問題描述   題目很簡單,給出N個數字,不改變它們的相對位置,在中間加入K個乘號和N-K-1個加號,(括號隨便加)使最終結果儘量大。因為乘號和加號一共就是N-1個了,所以恰好每兩個相鄰數字之間都有一個符號。例如:

解決移動端rem載入瞬間頁面錯亂的方法放大或者縮小

頁面在載入未完成前會出現瞬間錯亂的現象,雖然時間不算長,但是肉眼可見,必須解決 我們知道頁面載入順序通常是”從上往下”載入的,所以在內容區域,也就是body以及body包含的DOM還未被瀏覽器遍歷之前

JAVA中陣列的記憶體棧和堆

JAVA對記憶體空間的劃分 五部分:棧 堆 方法區 本地方法區 暫存器 今天主要談棧和堆 棧記憶體:儲存的都是區域性變數。 只要是在方法中定義的變數都是區域性變數,一旦變數的生命週期結束,該變數就被釋放。 (壓棧彈棧 balabalabala) 堆記

Java學習9:super隱式引數關鍵字記憶體分析詳解及用法

super關鍵字,是一個隱式引數(另一個隱式引數是this)。 注:super關鍵字和this關鍵字極為類似,學習時可參考this關鍵字用法。this(隱式引數)關鍵字記憶體分析詳解及用法 1.概述 super是直接父類的引用(this是當前物件的引用

購物籃分析分類演算法——頻繁模式挖掘聚類演算法

        頻繁模式是頻繁地出現在資料集中的模式,包括頻繁項集(如牛奶和麵包)、頻繁子序列(首先購買PC,然後是數碼相機,再後是記憶體卡)或頻繁子結構(涉及不同的結構形式,如子圖、子樹或子格,它可

紅包分配演算法年後寫的

今天正月十一,春節已經過去了10天,紅包想必大家都已經搶很多了,不知道大家有沒有想過這個紅包怎麼實現分配的呢? 為什麼有的人分這麼多,有的人分那麼多? 到底是怎麼實現的呢? 答案是,我也不知道。 現在最火的就是微信紅包了,當然我們也不可能知道微信紅包

頻繁模式挖掘Frequent Pattern Mining

       頻繁模式挖掘(FrequentPatternMining)是資料探勘中很常用的一個種挖掘,今天給大家介紹的一種名叫Apriori的頻繁模式挖掘演算法。先來看看什麼叫頻繁模式?~就是經常一起出現的模式,這裡的“模式”是一個比較抽象的概念,我們來看一個具體的例子,

Drawable和Bitmap在記憶體中誰更省記憶體轉自u010436741

08-28 10:15:27.009: E/(11960): 測試第211張圖片 08-28 10:15:27.024: E/(11960): 測試第212張圖片 08-28 10:15:27.034: E/(11960): 測試第213張圖片 08-28 10:15:27.049: E/(11960): 測

Android 之 三級快取記憶體!!!、本地、網路記憶體LruCache擴充套件 及原始碼分析--- 學習和程式碼講解

一. 三級快取簡介 如上圖所示,目前App中UI介面經常會涉及到圖片,特別是像“今日關注”新聞這類app中,圖片運用的機率十分頻繁。當手機上需要顯示大量圖片類似listView、gridView控制元件並且使用者會上下滑動,即將瀏覽過的圖片又載入一遍,