1. 程式人生 > >技術系列之 記憶體管理(三)

技術系列之 記憶體管理(三)

(2)boost::pool系列。boost的記憶體池最低層是simple_segregated_storage,類似於Loki中的chunk,在其中申請釋放block(boost中把block稱為chunk,暈死,這裡還是稱其為block)採用了和loki的chunk中同樣的演算法,不同的是simple_segregated_storage使用void*儲存block的塊序號,loki中使用char,因此boost中的simple_segregated_storage沒有255的上限限制,自然也就不需要再其上再封裝一層類似與FixedAllocator的層面。另boost沒有遮蔽塊的大小,直接提供定長的介面給使用者,省掉了SmallObjAllocator層面。因此boost的記憶體池申請釋放block的時間複雜度都是O(1)(object_pool和pool_allocator除外),另避免的小記憶體的浪費,同時boost不能象loki那樣在將block歸還給記憶體池的時候根據chunk的空閒數量釋放記憶體歸還給系統,只能顯式呼叫釋放記憶體函式或者等記憶體池銷燬的時候,基本上和記憶體池生命週期內永不釋放沒什麼區別。
    boost的最低層是simple_segregated_storage,主要演算法和loki中的chunk一樣,不多說了。這裡說下影響上層介面的兩類實現:add_block/malloc/free、add_ordered_block/malloc/ordered_free,兩種低層實現造成boost上層設計的成功與失敗,前者效率高,和loki一樣直接增加釋放,時間複雜度O(1),後者掃描排序,時間複雜度O(n)。
    boost提供了四種記憶體池模型供使用:pool、object_pool、singleton_pool、pool_allocator/fast_pool_allocator。
1)pool


基本的定長記憶體池

#include <boost/pool/pool.hpp>
typedef struct student_st
{
   
char name[10];
   
int age;
}
CStudent;
int main()
{
   boost::pool
<> student_pool(sizeof(CStudent));
   CStudent 
*const obj=(CStudent *)student_pool.malloc();
   student_pool.free(obj);
   
return0;
}

    pool的模版引數只有一個分配子型別,boost提供了兩種default_user_allocator_new_delete/default_user_allocator_malloc_free,指明申請釋放記憶體的時候使用new/delete,還是malloc/free,預設是default_user_allocator_new_delete。建構函式有2個引數:nrequested_size,nnext_size。nrequested_size是block的大小(因為void*儲存序號,因此boost內建了block的最小值,nrequested_size過小則取內建值),nnext_size是simple_segregated_storage中記憶體不足的時候,申請的block數量,預設是32。最全面的例項化pool類似這樣:boost::pool<boost::default_user_allocator_malloc_free> student_pool(sizeof(CStudent),255);
    pool提供的函式主要有:

malloc/free  基於add_block/malloc/free實現,高效
ordered_malloc/ordered_free 基於add_ordered_block/malloc/ordered_free實現,在pool中無任何意義,切勿使用。
release_memory/purge_memory 前者釋放池中未使用記憶體,後者釋放池中所有記憶體。另池析構也會釋放記憶體

2)object_pool

物件記憶體池,這是最失敗的一個記憶體池設計。 #include <boost/pool/object_pool.hpp>

class A{
public:
   A():data_(
0){}
private
:
   
int data_;
}
;
int main()
{
   boost::object_pool
<A> obj_pool;
   A 
*const pA=obj_pool.construct();
   obj_pool.destroy(pA);
   
return0;
}

    object_pool繼承至pool,有兩個模版引數,第一個就是物件型別,第二個是分配子型別,默認同pool是default_user_allocator_new_delete。建構函式引數只有nnext_size,意義以及預設值同pool。最全面的例項化object_pool類似這樣:boost::pool<A,boost::default_user_allocator_malloc_free> obj_pool(255);
object_pool提供的函式主要有(繼承至父類的略):

malloc/free 複寫pool的malloc/free,add_ordered_block/malloc/ordered_free實現
construct/destroy 基於本類的malloc/free實現,額外呼叫預設建構函式和預設解構函式。
~object_pool 單獨拿出這個說下,若析構的時候有物件未被destroy,可以檢測到,釋放記憶體前對其執行destroy
    為什麼boost::object_pool要設計成這樣?能呼叫建構函式和解構函式顯然不是boost::object_pool類設計的出發點,因為建構函式只能執行預設建構函式(首次發表錯誤:可以呼叫任意的建構函式,參見程式碼檔案:boost/pool/detail/pool_construct.inc和boost/pool/detail/pool_construct_simple.inc,感謝eXile指正),近似於無,它的重點是記憶體釋放時候的清理工作,這個工作預設的解構函式就足夠了。apr_pool記憶體池中就可以註冊記憶體清理函式,在釋放記憶體的時刻執行關閉檔案描述符、關閉socket等操作。boost::object_pool也想實現同樣的功能,因此設計了destroy這個函式,而同時為了防止使用者遺漏掉這個呼叫,而又在記憶體池析構的時候進行了檢測回收。為了這個目的而又不至於析構object_pool的時間複雜度是O(n平方),boost::object_pool付出了沉重的代價,在每次的destoy都執行排序功能,時間複雜度O(n),最後析構的時間複雜度是O(n),同樣為了這個目的,從simple_segregated_storage增加了add_ordered_block/ordered_free,pool增加了ordered_malloc/ordered_free等累贅多餘的功能。
    基於上面討論的原因,boost::object_pool被設計成了現在的樣子,成了一個雞肋類。類的設計者似乎忘記了記憶體池使用的初衷,忘記了記憶體池中記憶體申請釋放的頻率很高,遠遠大於記憶體池物件的析構。如果你依然想使用類似於此的記憶體清理功能,可以在boost::object_pool上修改,不復寫malloc/free即可,重寫object_pool的析構,簡單釋放記憶體就好,因此析構object_pool前不要忘記呼叫destroy,這也是使用placement new預設遵守的規則,或者保持以前的解構函式,犧牲析構時的效能。placement new的作用是為已經申請好的記憶體呼叫建構函式,使用流程為(1)申請記憶體buf(2)呼叫placement new:new(buf)construtor()(3)呼叫析構destructor()(4)釋放記憶體buf。#include<new>可以使用placement new。
3)singleton_pool
pool的加鎖版本。 #include <boost/pool/singleton_pool.hpp>
typedef struct student_st
{
   
char name[10];
   
int age;
}
CStudent;
typedef struct singleton_pool_tag
{}singleton_pool_tag;
int main()
{
   typedef boost::singleton_pool
<singleton_pool_tag,sizeof(CStudent)>  global;
   CStudent 
*const df=(CStudent *)global::malloc();
   global::free(df);
   
return0;
}

    singleton_pool為單例類,是對pool的加鎖封裝,適用於多執行緒環境,其中所有函式都是靜態型別。它的模版引數有5個,tag:標記而已,無意義;RequestedSize:block的長度;UserAllocator:分配子,預設還是default_user_allocator_new_delete;Mutex:鎖機制,預設值最終依賴於系統環境,linux下是pthread_mutex,它是對pthread_mutex_t的封裝;NextSize:記憶體不足的時候,申請的block數量,預設是32。最全面的使用singleton_pool類似這樣:typedef boost::singleton_pool<singleton_pool_tag,sizeof(CStudent),default_user_allocator_new_delete,details::pool::default_mutex,200>  global;
    它暴露的函式和pool相同。
4)pool_allocator/fast_pool_allocator
    stl::allocator的替換方案。兩者都是基於singleton_pool實現,實現了stl::allocator要求的介面規範。兩者的使用相同,區別在於pool_allocator的實現呼叫ordered_malloc/ordered_free,fast_pool_allocator的實現呼叫malloc/free,因此推薦使用後者。

#include <boost/pool/pool_alloc.hpp>
#include 
<vector>
typedef struct student_st
{
 
char name[10];
 
int age;
}
CStudent;

int main()
{
  std::vector
<CStudent *,boost::fast_pool_allocator<CStudent *>> v(8);
  CStudent 
*pObj=new CStudent();
  v[
1]=pObj;
  boost::singleton_pool
<boost::fast_pool_allocator_tag,sizeof(CStudent *)>::purge_memory(); 
  
return0;
}

    fast_pool_allocator的模版引數有四個:型別,分配子,鎖型別,記憶體不足時的申請的block數量,後三者都有預設值,不再說了。它使用的singleton_pool的tag是boost::fast_pool_allocator_tag。
評價:boost::pool小巧高效,多多使用,多執行緒環境下使用boost::singleton_pool,不要使用兩者的ordered_malloc/ordered_free函式。boost::object_pool不建議使用,可以改造後使用。pool_allocator/fast_pool_allocator推薦使用後者。


未完 待續.................... 不過這個主題暫時不寫了 等有時間了

相關推薦

【原創】技術系列 記憶體管理

作者:CppExplore 地址:http://www.cppblog.com/CppExplore/(2)boost::pool系列。boost的記憶體池最低層是simple_segregated_storage,類似於Loki中的chunk,在其中申請釋放block(boost中把block稱為c

技術系列 記憶體管理

(2)boost::pool系列。boost的記憶體池最低層是simple_segregated_storage,類似於Loki中的chunk,在其中申請釋放block(boost中把block稱為chunk,暈死,這裡還是稱其為block)採用了和loki的chunk中同樣

技術系列 記憶體管理

2、定長記憶體池。典型的實現有LOKI、BOOST。特點是為不同型別的資料結構分別建立記憶體池,需要記憶體的時候從相應的記憶體池中申請記憶體,優點是可以在使用完畢立即把記憶體歸還池中,可以更為細粒度的控制記憶體塊。    與變長的相比,這種型別的記憶體池更加通用,另一方面對於

zabbix系列郵件告警

zabbix 郵件告警 設置郵件告警有兩種方式:1)、通過Linux自帶的mail發送告警郵件2)、通過第三方郵箱發送(如QQ郵箱、163郵箱等)告警郵件1、修改 zabbx_server.conf 文件,指定腳本路徑,沒有則添加[root@centos1 ~]# vim /usr/local/zab

Android 註解系列APT工具

該文章中涉及的程式碼,我已經提交到GitHub上了,大家按需下載---->原始碼 前言 在上篇文章Android 註解系列之Annotation(二)中,簡要的介紹了註解的基本使用與定義。同時也提出了以下幾個問題,當我們聲明瞭一個註解後,是不是需要手動找到所有的Class

linux基本服務系列智慧DNS

前言 上一期講了利用bind+mysql的結合,打造一個數據庫管理的一個DNS系統,從而能減少運維的維護量;這期再講講增加dns遠端管理的功能和外部解析能力,可以進一步舒服的維護DNS系統。 rndc遠端控制服務 1、在被管理的伺服器上生成金鑰 # cd /var/named/chroo

【原創】技術系列 狀態機

與常規狀態機相比,它的FSM_STATE結構沒有default_func,多了 FSM_STATE_ID parent; FSM_STATE_ID default_child;兩個結構。狀態機初始化的時候可以指定預設狀態,為了防止指定的狀態非葉結點,增加fsm_init方法。該狀態機的事件處理演算

【原創】技術系列 執行緒

作者:CppExplore 網址:http://www.cppblog.com/CppExplore/廢話不多說,詳細介紹使用執行緒的優點好處請參考baidu、google。一、執行緒使用場景。使用執行緒的方式大致有兩種:(1)流水線方式。根據業務特點,將一個流程的處理分割成多個執行緒,形成流水線的處

【原創】技術系列 狀態機

作者:CppExplore 網址:http://www.cppblog.com/CppExplore/一、狀態機描述狀態機理論最初的發展在數位電路設計領域。在數位電路方面,根據輸出是否與輸入訊號有關,狀態機可以劃分為Mealy型和Moore型狀態機;根據輸出是否與輸入訊號同步,狀態機可以劃分為非同步和

Android-PickerView系列封裝篇

注:此文交由跟我一起維護Android-PickerView 專案的夥伴totcw寫的,友情連結:http://blog.csdn.net/totcw 一、概述 二、封裝   這是一張類的關係圖 建構函式使用的Builder設計模式來封裝的,通過builder

初探jvm虛擬機器記憶體管理

本篇記錄一些概念性的東西,後續結合例項分析虛擬機器的記憶體機制。java虛擬機器在程式執行時將記憶體劃分為以下幾個區域,每個區域作用,生命週期各不相同程式計數器 虛擬機器棧 本地方法棧 方法區 堆程式計數器 執行緒執行的位元組碼行號指示器。 多執行緒是通過時間片輪轉法獲取cp

技術系列 網路模型基礎篇

全文針對linux環境。tcp/udp兩種server種,tcp相對較複雜也相對比較常用。本文就從tcp server開始講起。先從基本說起,看一個單執行緒的網路模型,處理流程如下: socket-->bind-->listen-->[accept-->

記憶體管理tcmalloc1 記憶體分配及原始碼剖析

    本來打算花一天時間看看tcmalloc就算結束了。但是在網上找部落格的時候發現,100個人有101鐘不同的解釋,完全沒有公論。一怒之下,剖原始碼!         tcmalloc是對ptmalloc的升級版。和ptmalloc相比,tcmalloc對於小塊記憶體

技術系列 網路模型

作者:CppExplore 網址:http://www.cppblog.com/CppExplore/本章主要列舉伺服器程式的各種網路模型,示例程式以及效能對比後面再寫。一、分類依據。伺服器的網路模型分類主要依據以下幾點(1)是否阻塞方式處理請求,是否多路複用,使用哪種多路複

技術系列 定時器

一、 基礎知識1、時間型別。Linux下常用的時間型別有4個:time_t,struct timeval,struct timespec,struct tm。(1)time_t是一個長整型,一般用來表示用1970年以來的秒數。(2)Struct timeval有兩個成員,一個

技術系列 執行緒

為了後面寫的《網路模型(二)》,多寫一篇關於執行緒的。執行緒使用涉及的主要資料結構以及應用框架可以參考http://www.cppblog.com/CppExplore/archive/2008/01/15/41175.html。本文的主要目的是給出linux下實用的執行緒訊

JAVA 虛擬機器記憶體管理

JMM ( Java Memory Model )概要 如圖:為java執行時資料區,在程式執行過程中對記憶體的分配、垃圾的回收都在執行時資料區中進行。 方法區和堆被執行緒所共享(藍色區塊) JVM棧、本地方法棧、程式計數器為執行緒所獨享(綠色區塊)

MVC系列博客排球計分模型類的實現

layers 自動生成 ext alt 感覺 名稱 數據 string 後來 最初我使用的是連接數據庫的方法來建立數據連接的,後來聽了同學用EF框架來生成數據庫自動連接,感覺很好用,然後我就重新用EF框架生成數據庫 使用EF框架生成數據庫,要有相應的模型類,模型類如下:

linux程序管理程序建立

在linux系統中,許多程序在誕生之初都與其父程序共同用一個儲存空間。但是子程序又可以建立自己的儲存空間,並與父程序“分道揚鑣”,成為與父程序一樣真正意義上的程序。  linux系統執行的第一個程序是在初始化階段“捏造出來的”。而此後的執行緒或程序都是由一個已存在的程序像細胞分裂一樣通過系統呼叫複

複習spring基礎——Spring 的事務管理—配置AOP事務XML和註解方式

事務 事務邏輯上的一組操作,組成這組操作的各個邏輯單元,要麼一起成功,要麼一起失敗 事務的特性 原子性 :強調事務的不可分割. 一致性 :事務的執行的前後資料的完整性保持一致. 隔離性 :一個事務執行的過程中,不應該受到其他事務的干擾. 永續性 :事務一旦結束