1. 程式人生 > >記憶體池技術暢想

記憶體池技術暢想

內容:

本文將介紹幾種常用的記憶體池技術的實現,這是我最近學習各大開源的記憶體池技術遺留下來的筆記,其主要內容包括:
  • STL 記憶體池以及類 STL 記憶體池實現
  • Memcached 記憶體池實現
  • 固定規格記憶體池實現
  • Nginx 記憶體池實現

一. 類 STL 的記憶體池實現方式

SGI STL 的記憶體池分為一級配置器和二級配置器:

一級配置器主要處理分配空間大小大於 128Byte 的需求,其內部實現就是直接使用 malloc realloc 和 free.

二級配置器則使用使用 free_list 的陣列連結串列的方式來管理記憶體,SGI 的 Allocate 最小的分辨單位為 8Byte, 其 free_list 陣列存著 8*n(n=1…16) 大小記憶體的首地址, 大小同樣的記憶體塊使用連結串列的形式相連

free_list[0] ——–> 8 byte

free_list[1] ——–> 16 byte

free_list[2] ——–> 24 byte

free_list[3] ——–> 32 byte

free_list[15] ——-> 128 byte

因為其對記憶體的管理的最小分辨度為 8Byte, 所以當我們申請的記憶體空間不是 8 的倍數的時候,記憶體池會將其調整為 8 的倍數大小,這叫記憶體對齊。當然這也免不了帶來記憶體浪費,例如我們只需要一個 10Byte 的大小,記憶體池經過記憶體對齊後,會給我們一個 16Byte 的大小,而剩餘的 6Byte,在這次使用中根本沒有用到。(對於 chunk_allocate 的優化請見探究作業系統的記憶體分配(malloc)對齊策略一文的末尾處)

類 STL 的記憶體池一般都有如下 API

void* allocate(size_t __n) // 外部 API,分配記憶體
void deallocate(void* __p, size_t __n)// 外部 API,回收記憶體,以供再利用
char* chunk_alloc(size_t __size, int& __nobjs)// 內部函式,用於分配一個大塊
void* refill(size_t n) // 內部函式,用於 allocate 從 free_list 中未找到可使用的塊時呼叫

這種記憶體池的工作流程大致如下:

  • 外部呼叫 allocate 向記憶體池申請記憶體
  • allocate 通過記憶體對齊的方式在 free_list 找到合適的記憶體塊連結串列頭
  • 判斷連結串列頭是否為 NULL, 為 NULL 則表示沒有此規格空閒的記憶體,如果不為 NULL,則返那塊記憶體地址,並將此塊記憶體地址移除它對應的連結串列
  • 如果為 NULL,則呼叫 refill 在 freelist 上掛載 20 個此規格的記憶體空間(形成連結串列),也就是保證此規格的記憶體空間下次請求時夠用
  • refill 的內部呼叫了 chunk_alloc 函式,chunk_alloc 的職責就是負責記憶體池的所有記憶體的生產,在生產的時候他為了保證下次能有記憶體用,所以會將空間 * 2,所以這個申請流程總的記憶體消耗為:(對需求規格記憶體對齊後的大小)*20*2
下面舉一個例子來簡單得說明一下:
  •     當第一次呼叫chunk_alloc(32,10)的時候,表示我要申請10塊__Obje(free_list), 每塊大小32B,此時,記憶體池大小為0,從堆空間申請32*20的大小的記憶體,把其中32*10大小的分給free_list[3]。
  •    我再次申請64*5大小的空間,此時free_list[7]為0, 它要從記憶體池提取記憶體,而此時記憶體池剩下320B,剛好填充給free_list[7],記憶體池此時大小為0。
  •    第三次請求72*10大小的空間,此時free_list[8]為0,它要從記憶體池提取記憶體,此時記憶體池空間不足,再次從堆空間申請72*20大小的空間,分72*10給free_list用。

首次申請20Byte後的狀態圖:

在未設定預分配的STL記憶體池中,某個中間狀態的整體圖

由於STL原始碼可閱讀性不強,各種巨集等等滿目不堪,所以我這裡就不貼SGI 的原始碼了,我在這裡貼一個簡單易懂的山寨版本, 基本的思路是一模一樣的,這個實現沒有了一級和二級配置器,而是在需要的時候直接malloc或者從free_list找。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 #ifndef MEMORYPOOL_H#define MEMORYPOOL_H#include <stdio.h>#include <assert.h>using namespacestd;classMemoryPool{private:// Really we should use static const int x = N// instead of enum {x = N}, but few compilers accept the former.enum{__ALIGN=8};// 小型區塊的上調邊界,即小型記憶體塊每次上調 8byteenum{__MAX_BYTES=128};// 小型區塊的上界enum{__NFREELISTS=__MAX_BYTES/__ALIGN};//free-lists 的個數,為: 16,每個 free-list 管理不同大小記憶體塊的配置// 將請求的記憶體大小上調整為 8byte 的倍數,比如 8byte, 16byte, 24byte, 32bytestaticsize_t ROUND_UP(size_t bytes){return(((bytes)+__ALIGN-1)&~(__ALIGN-1));}unionobj{union obj*free_list_link;// 下一個區塊的記憶體地址,如果為 NULL,則表示無可用區塊charclient_data[1];// 記憶體區塊的起始地址          };private:staticobj *free_list[__NFREELISTS];// __NFREELISTS = 16/*        free_list[0] --------> 8 byte(free_list[0] 管理 8bye 區塊的配置)        free_list[1] --------> 16 byte        free_list[2] --------> 24 byte        free_list[3] --------> 32 byte        ... ...        free_list[15] -------> 128 byte    */// 根據區塊大小,決定使用第 n 號的 free_list。n = [0, 15] 開始staticsize_t FREELIST_INDEX(size_t bytes){return(((bytes)+__ALIGN-1)/__ALIGN-1);}// Returns an object of size n, and optionally adds to size n free list.staticvoid*refill(size_tn);// 配置一大塊空間,可容納 nobjs 個大小為 size 的區塊// 如果配置 nobjs 個區塊有所不便,nobjs 可能會降低staticchar*chunk_alloc(size_t size,int&nobjs);// Chunk allocation state.staticchar*start_free;// 記憶體池起始位置staticchar*end_free;// 記憶體池結束位置staticsize_t heap_size;// 記憶體池的大小public:// 公開介面,記憶體分配函式     staticvoid*allocate(size_tn){obj**my_free_list=NULL;obj*result=NULL;// 如果待分配的記憶體位元組數大於 128byte, 就呼叫 C 標準庫函式 mallocif(n>(size_t)__MAX_BYTES){returnmalloc(n);}// 調整 my_free_lisyt,從這裡取使用者請求的區塊my_free_list=free_list+FREELIST_INDEX(n);result=*my_free_list;// 欲返回給客戶端的區塊if(result==0)// 沒有區塊了{void*r=refill(ROUND_UP(n));returnr;}*my_free_list=result->free_list_link;// 調整連結串列指標,使其指向下一個有效區塊returnresult;};// 歸還區塊staticvoiddeallocate(void*p,size_tn){assert(p!=NULL);obj*q=(obj *)p;obj**my_free_list=NULL;// 大於 128byte 就呼叫第一級記憶體配置器if(n>(size_t)__MAX_BYTES){free(p);}// 尋找對應的 free_listmy_free_list=free_list+FREELIST_INDEX(n);// 調整 free_lis,回收記憶體q->free_list_link=*my_free_list;*my_free_list=q;}staticvoid*reallocate(void*p,size_t old_sz,size_t new_sz);};/* We allocate memory in large chunks in order to avoid fragmenting     *//* the malloc heap too much.                                            *//* We assume that size is properly aligned.                             *//* We hold the allocation lock.                                         */// 假設 size 已經上調至 8 的倍數// 注意 nobjs 是 passed by reference, 是輸入輸出引數char*MemoryPool::chunk_alloc(size_t size,int&nobjs){char*result=NULL;size_t total_bytes=size *nobjs;// 請求分配記憶體塊的總大小size_t bytes_left=end_free-start_free;// 記憶體池剩餘空間的大小if(bytes_left>=total_bytes)// 記憶體池剩餘空間滿足要求量{result=start_free;start_free

相關推薦

記憶體技術暢想

內容: 本文將介紹幾種常用的記憶體池技術的實現,這是我最近學習各大開源的記憶體池技術遺留下來的筆記,其主要內容包括: STL 記憶體池以及類 STL 記憶體池實現 Memcached 記憶體池實現 固定規格記憶體池實現 Nginx 記憶體池實現 一. 類

常見C++記憶體技術

總結下常見的C++記憶體池,以備以後查詢。應該說沒有一個記憶體池適合所有的情況, 根據不同的需求選擇正確的記憶體池才是正道.(1)最簡單的固定大小緩衝池    適用於頻繁分配和釋放固定大小物件的情況, 關於這個記憶體池,我這裡總結過:一個高效的記憶體池實現(2)dlmalloc      應該來說

記憶體——第一章 幾種常用的記憶體技術

#define EXTRA_BUFFER_SIZE        64 namespace easy {     template<class _Type,class _Alloc >     class EasyRingbuffer      {   

提高C++效能的程式設計技術筆記:多執行緒記憶體+測試程式碼

為了使多個執行緒併發地分配和釋放記憶體,必須在分配器方法中新增互斥鎖。 全域性記憶體管理器(通過new()和delete()實現)是通用的,因此它的開銷也非常大。 因為單執行緒記憶體管理器要比多執行緒記憶體管理器快的多,所以如果要分配的大多數記憶體塊限於單執行緒中使用,那麼可以顯著提升效

提高C++效能的程式設計技術筆記:單執行緒記憶體+測試程式碼

頻繁地分配和回收記憶體會嚴重地降低程式的效能。效能降低的原因在於預設的記憶體管理是通用的。應用程式可能會以某種特定的方式使用記憶體,並且為不需要的功能付出效能上的代價。通過開發專用的記憶體管理器可以解決這個問題。對專用記憶體管理器的設計可以從多個角度考慮。我們至少可以想到兩個方面:大小和併發。

java數據庫連接技術簡單使用

util mysql 一個 dsta getc lean 創建 gin eat JDBCDemo.java: package com.itheima.jdbc; import java.sql.Connection; import java.sql.PreparedS

實現連接技術的樣例

啟動 vax oca 找到 max 安裝文件 通過 pass contex 1. 在tomcat的安裝文件夾下conf文件夾下的context.xml文件加入例如以下代碼: <Resource name="jdbc/course" aut

Java對象技術的原理及其實現

問題 多種方式 等待 具體實現 tex sin 工作 程序 collect Java對象的生命周期分析  Java對象的生命周期大致包括三個階段:對象的創建,對象的使用,對象的清除。因此,對象的生命周期長度可用如下的表達式表示:T = T1 + T2 +T3。其中T1

連接技術 Connection Pooling

margin image src 原創 max lan 池技術 cti load 原創地址:http://www.cnblogs.com/jfzhu/p/3705703.html轉載請註明出處 和數據庫建立一個物理連接是一個很耗時的任務,所以無論是ADO.NET還是J2EE

基於SmartThreadPool線程技術實現多任務批量處理

C# .NET 多線程技術 Thread SmartThreadPool 一、多線程技術應用場景介紹本期同樣帶給大家分享的是阿笨在實際工作中遇到的真實業務場景,請跟隨阿笨的視角去如何采用基於開源組件SmartThreadPool線程池技術實現多任務批量處理。在工作中您是否遇到過如何快速高效

數據庫連接技術

管理機 超過 等待隊列 優勢 初始化 連接數量 占用 斷開 http 一、基本原理 連接池基本的思想是在系統初始化的時候,將數據庫連接作為對象存儲在內存中,當用戶需要訪問數據庫時,並非建立一個新的連接,而是從連接池中取出一個已經建立的空閑連接對象。使用完畢後,用戶也並非將連

java:記憶體、程序、執行緒

記憶體池: 自定義記憶體池的思想通過這個"池"字表露無疑,應用程式可以通過系統的記憶體分配呼叫預先一次性申請適當大小的記憶體作為一個記憶體池,之後應用程式自己對記憶體的分配和釋放則可以通過這個記憶體池來完成。 只有當記憶體池大小需要動態擴充套件時,才需要再呼叫系統的記憶體分配函式,其他時間對

【原始碼剖析】MemoryPool —— 簡單高效的記憶體 allocator 實現

      什麼是記憶體池?什麼是 C++ 的 allocator?       記憶體池簡單說,是為了減少頻繁使用 malloc/free new/delete 等系統呼叫而造成的效能損耗而設計的。當我們的程式需要頻繁地申請和釋放

Unity簡單物件緩衝技術

一、建立物件緩衝池指令碼 /*** * * Title: * 預載入與物件緩衝池技術 * * 物件緩衝池管理器 * * Description: * 基本原理: *

PooledByteBuf記憶體-------這個我現在不太懂

轉載自:http://blog.csdn.net/youaremoon/article/details/47910971              http://blog.csdn.net/youaremoon/article/detail

簡單的執行緒技術,詮釋佇列的架構邏輯

    大家是不是對執行緒池的技術還是不夠明白啦?相信看完後大家會有不一樣的體驗!!!      對於服務端的程式,進場面對的是客戶端的請求,那麼如果客戶端傳來的內容比較單一,內容比較小,需要服務端快速的處理並返回結果;如果把每

SGISTL原始碼閱讀三 空間配置器下(記憶體memory pool)

SGISTL原始碼閱讀三 空間配置器下(記憶體池memory pool) 前言 在上一個部落格我們講述了空間配置器的第二級配置器,它的關鍵點free-lists是依賴於記憶體池的。在refill中我們通過呼叫chunk_alloc函式來申請區塊,chunk_alloc的作用就是從記憶

[nginx] nginx記憶體ngx_pool_t的介紹

本文詳細介紹nginx中記憶體池的設計和實現。   nginx pool 由 小記憶體拉鍊、大記憶體拉鍊、回撥函式拉鍊組成。建立的第一個ngx_pool_s頭部,會作為整個pool的head,儲存一些拉鍊資訊。 1 建立一個ngx_pool: struct ngx_po

高效記憶體的設計方案 C語言

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

記憶體的設計和實現 -- C++應用程式效能優化

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!