iOS標(biāo)準(zhǔn)庫中常用數(shù)據(jù)結(jié)構(gòu)和算法之內(nèi)存池

上一篇:iOS標(biāo)準(zhǔn)庫中常用數(shù)據(jù)結(jié)構(gòu)和算法之位串

??內(nèi)存池

內(nèi)存池提供了內(nèi)存的復(fù)用和持久的存儲功能。設(shè)想一個場景,當(dāng)你分配了一塊大內(nèi)存并且填寫了內(nèi)容揭绑,但是你又不是經(jīng)常去訪問這塊內(nèi)存怜庸。這樣的內(nèi)存利用率將不高,而且無法復(fù)用征椒。而如果是采用內(nèi)存池則可以很輕松解決這個問題:你只需要從內(nèi)存池中申請這塊內(nèi)存,設(shè)置完內(nèi)容后當(dāng)不需要用時你可以將這塊內(nèi)存放入內(nèi)存池中湃累,供其他地方在申請時進(jìn)行復(fù)用勃救,而當(dāng)你再次需要時則只需要重新申請即可。內(nèi)存池提供了內(nèi)存分配編號而且設(shè)置臟標(biāo)志的概念治力,當(dāng)你把分配的內(nèi)存放入內(nèi)存池并設(shè)置臟標(biāo)志后蒙秒,系統(tǒng)就會在適當(dāng)?shù)臅r候?qū)⑦@塊內(nèi)存的內(nèi)容寫回到磁盤,這樣當(dāng)你再次根據(jù)內(nèi)存編號來訪問內(nèi)存時宵统,系統(tǒng)就又會從磁盤中將內(nèi)容讀取到內(nèi)存中去晕讲。

功能:在iOS中提供了一套內(nèi)存池管理的API,你可以用這套API來實現(xiàn)上述的功能榜田,而且系統(tǒng)內(nèi)部很多功能也是借助內(nèi)存池來實現(xiàn)內(nèi)存復(fù)用和磁盤存儲的益兄。
頭文件: #include <mpool.h>, #include <db.h>
平臺: BSD系統(tǒng),linux系統(tǒng)

一箭券、內(nèi)存池的創(chuàng)建净捅、同步和關(guān)閉

功能:創(chuàng)建和關(guān)閉一個內(nèi)存池對象并和磁盤文件綁定以便進(jìn)行同步操作。
函數(shù)簽名


//創(chuàng)建一個內(nèi)存池對象
 MPOOL * mpool_open(void *key, int fd, pgno_t pagesize, pgno_t maxcache);

//將內(nèi)存池中的臟數(shù)據(jù)同步寫回到磁盤文件中
int mpool_sync(MPOOL *mp);

//關(guān)閉和銷毀內(nèi)存池對象辩块。
int mpool_close(MPOOL *mp);

參數(shù)
key:[in] 保留字段蛔六,暫時沒有用處,傳遞NULL即可废亭。
fd:[in] 內(nèi)存池關(guān)聯(lián)的磁盤文件句柄国章,文件句柄需要用open函數(shù)來打開。
pagesize:[in] 內(nèi)存池中每次申請和分配的內(nèi)存的尺寸大小豆村,單位是字節(jié)液兽。
maxcache:[in] 內(nèi)存池中內(nèi)存頁的最大緩存數(shù)量。如果池中緩存的內(nèi)存數(shù)量超過了最大緩存的數(shù)量就會復(fù)用已經(jīng)存在的內(nèi)存掌动,而不是每次都分配新的內(nèi)存四啰。
return:[out] 返回一個新創(chuàng)建的內(nèi)存池對象,其他兩個函數(shù)成功返回0粗恢,失敗返回非0.

描述

  1. 內(nèi)存池中的內(nèi)存的分配和獲取是以頁為單位進(jìn)行的柑晒,每次分配的頁的尺寸大小由pagesize指定,同時內(nèi)存池也指定了最大的緩存頁數(shù)量maxcache眷射。每次從內(nèi)存池中分配一頁內(nèi)存時匙赞,除了會返回分配的內(nèi)存地址外佛掖,還會返回這一頁內(nèi)存的編號。這個編號對于內(nèi)存池中的內(nèi)存頁來說是唯一的涌庭。因為內(nèi)存池中的內(nèi)存是可以被復(fù)用的芥被,因此就有可能是不同的編號的內(nèi)存頁所得到的內(nèi)存地址是相同的。

  2. 每一個內(nèi)存池對象都會要和一個文件關(guān)聯(lián)起來脾猛,以便能夠?qū)崿F(xiàn)內(nèi)存數(shù)據(jù)的永久存儲和內(nèi)存的復(fù)用撕彤。文件句柄必須用open函數(shù)來打開,比如下面的例子:

 int fd = open("/Users/apple/mpool", O_RDWR|O_APPEND|O_CREAT,0660);
  1. 當(dāng)我們不需要使用某個內(nèi)存頁時或者內(nèi)存頁的內(nèi)容有改動則我們需要將這個內(nèi)存頁放入回內(nèi)存池中猛拴,并將頁標(biāo)志為臟(DIRTY)。這樣系統(tǒng)就會在適當(dāng)?shù)臅r候?qū)⒋藘?nèi)存頁的數(shù)據(jù)寫回到磁盤文件中蚀狰,同時此內(nèi)存頁也會在后續(xù)被重復(fù)利用愉昆。

  2. 當(dāng)我們想將所有設(shè)置為臟標(biāo)志的內(nèi)存頁立即寫入磁盤時則需要調(diào)用mpool_sync函數(shù)進(jìn)行同步處理。

  3. 當(dāng)我們不再需要內(nèi)存池時麻蹋,則可以通過mpool_close來關(guān)閉內(nèi)存池對象跛溉,需要注意的是關(guān)閉內(nèi)存池并不會將內(nèi)存中的數(shù)據(jù)回寫到磁盤中去。

二扮授、內(nèi)存池中內(nèi)存的獲取

功能: 從內(nèi)存池中申請分配一頁新的內(nèi)存或者獲取現(xiàn)有緩存中的內(nèi)存芳室。
函數(shù)簽名:

//從內(nèi)存池中申請分配一頁新的內(nèi)存
void *  mpool_new(MPOOL *mp, pgno_t *pgnoaddr);
//根據(jù)內(nèi)存編號頁獲取對應(yīng)的內(nèi)存。
void * mpool_get(MPOOL *mp, pgno_t pgno, u_int flags);

參數(shù):
mp:[in] 內(nèi)存池對象刹勃。
pgnoaddr:[out] 用于mpool_new函數(shù)堪侯,用于保存新分配的內(nèi)存頁編號。
pngno:[in] 用于mpool_get函數(shù)荔仁,指定要獲取的內(nèi)存頁的編號伍宦。
flags:[in] 此參數(shù)暫時無用。
return:[out] 返回分配或者獲取的內(nèi)存地址乏梁。如果分配或者獲取失敗則返回NULL次洼。
描述:

  1. 無論是new還是get每次從內(nèi)存池里面分配或者獲取的內(nèi)存頁的大小都是由上述mpool_open函數(shù)中的pagesize參數(shù)指定的大小。
  2. 系統(tǒng)內(nèi)部分配的內(nèi)存是用calloc函數(shù)實現(xiàn)的遇骑,但是我們不需要手動調(diào)用free來對內(nèi)存進(jìn)行釋放處理卖毁。
  3. 每個內(nèi)存頁都有一個唯一的頁編號,而且每次分配的頁編號也會一直遞增下去落萎。
  4. mpool_new函數(shù)申請分配新的內(nèi)存時亥啦,如果當(dāng)前緩存中的內(nèi)存頁小于maxcache數(shù)量則總是分配新的內(nèi)存,只有當(dāng)緩存數(shù)量大于maxcache時才會從現(xiàn)有的緩存中尋找一頁可以被重復(fù)利用的內(nèi)存頁模暗,如果沒有可以重復(fù)利用的頁面禁悠,則會繼續(xù)分配新的內(nèi)存頁。
  5. mpool_get函數(shù)則根據(jù)內(nèi)存頁的編號獲取對應(yīng)的內(nèi)存頁兑宇。如果編號不存在則返回NULL碍侦。需要注意的是一般在獲取了某一頁內(nèi)存后,不要進(jìn)行重復(fù)獲取操作,否則在DEBUG狀態(tài)下會返回異常瓷产。另外一個情況是有可能相同的頁編號下兩次獲取的內(nèi)存地址是不一樣的站玄,因為系統(tǒng)實現(xiàn)內(nèi)部有內(nèi)存復(fù)用的機制。
三濒旦、內(nèi)存池中內(nèi)存的放回

功能:將分配或者申請的內(nèi)存頁放回到內(nèi)存池中去株旷,以便進(jìn)行重復(fù)利用。
函數(shù)簽名:

int  mpool_put(MPOOL *mp, void *pgaddr, u_int flags);

參數(shù):
mp: [in] 內(nèi)存池對象尔邓。
pgaddr:[in] 要放入緩存的內(nèi)存頁地址晾剖。這個地址由mpool_get/new兩個函數(shù)返回。
flags:[in] 放回的屬性梯嗽,一般設(shè)置為0或者M(jìn)POOL_DIRTY齿尽。
return:[in] 函數(shù)調(diào)用成功返回0,失敗返回非0

描述

  1. 這個函數(shù)用來將內(nèi)存頁放入回內(nèi)存池緩存中灯节,以便對內(nèi)存進(jìn)行重復(fù)利用循头。當(dāng)將某個內(nèi)存地址放入回緩存后,將不能再次訪問這個內(nèi)存地址了炎疆。如果要想繼續(xù)訪問內(nèi)存中的數(shù)據(jù)則需要借助上述的mpool_get/new函數(shù)來重新獲取卡骂。
  2. flags:屬性如果指定為0時,表明放棄這次內(nèi)存中的內(nèi)容的修改形入,系統(tǒng)不會將內(nèi)存中的內(nèi)容寫入到磁盤中全跨,而只是將內(nèi)存放入緩存中供其他地方重復(fù)使用。而如果設(shè)置為MPOOL_DIRTY時唯笙,則表明將這頁內(nèi)存中的數(shù)據(jù)設(shè)置為臟標(biāo)志螟蒸,除了同樣將內(nèi)存放入緩存中重復(fù)利用外,則會在適當(dāng)?shù)臅r候?qū)?nèi)存中的數(shù)據(jù)寫入到磁盤中崩掘,以便下次進(jìn)行讀取七嫌。
四、內(nèi)存池磁盤讀寫通知

功能:注冊回調(diào)函數(shù)苞慢,當(dāng)某頁內(nèi)存要寫回到磁盤或者要從磁盤中讀取時就會調(diào)用指定的回調(diào)函數(shù)诵原。
函數(shù)簽名:

void mpool_filter(MPOOL *mp, void (*pgin)(void *, pgno_t, void *),
         void (*pgout)(void *, pgno_t, void *), void *pgcookie);

參數(shù):
mp:[in] 內(nèi)存池對象.
pgin: [in]: 回調(diào)函數(shù),當(dāng)某個內(nèi)存頁的數(shù)據(jù)需要從磁盤讀取時挽放,會在讀取完成后調(diào)用這個回調(diào)函數(shù)绍赛。
pgout:[in]: 回調(diào)函數(shù),當(dāng)某個內(nèi)存頁的數(shù)據(jù)要到磁盤時辑畦,會在寫入完成后調(diào)用這個回調(diào)函數(shù)吗蚌。
pgcookie: [in] 上述兩個回調(diào)函數(shù)的附加參數(shù)。
描述
因為內(nèi)存池中的內(nèi)存頁會進(jìn)行復(fù)用纯出,以及會在適當(dāng)?shù)臅r候?qū)?nèi)容同步到磁盤中蚯妇,或者從磁盤中將內(nèi)容讀取到內(nèi)存中敷燎,因此可以借助這個函數(shù)來監(jiān)控這些磁盤文件和內(nèi)存之間的讀寫操作。pgin和pgout函數(shù)的格式定義如下:

//pgin和pgout回調(diào)函數(shù)的格式箩言。
//pgcookie:是mpool_filter函數(shù)中傳入的參數(shù)硬贯。
//pgno: 要進(jìn)行讀寫的內(nèi)存頁編號
//pageaddr: 要進(jìn)行讀寫的內(nèi)存地址。
void (*pgcallback)(void *pgcookie, pgno_t pgno, void *pageaddr);

五陨收、實例代碼
#include <mpool.h>
#include <db.h>

 //創(chuàng)建并打開一個文件饭豹。
 int fd = open("/Users/apple/mpool", O_RDWR|O_APPEND|O_CREAT,0660);

//創(chuàng)建一個內(nèi)存池對象,每頁的內(nèi)存100個字節(jié)务漩,最大的緩存數(shù)量為4
 MPOOL *pool = mpool_open(NULL, fd, 100, 4);

   
//從內(nèi)存池中分配一個新的內(nèi)存頁拄衰,這里對返回的內(nèi)存填寫數(shù)據(jù)。
 pgno_t pidx1, pidx2 = 0;
 char *mem1 =  (char*)mpool_new(pool, &pidx1);
 memcpy(mem1, "aaa", 4);
    
 char *mem2 = (char*)mpool_new(pool, &pidx2);
 memcpy(mem2, "bbb", 4);
    
//將分配的內(nèi)存mem1放回內(nèi)存池中菲饼,但是內(nèi)容不保存到磁盤
 mpool_put(pool, mem1, 0);
//將分配的內(nèi)存mem2放回內(nèi)存池中肾砂,但是內(nèi)容保存到磁盤。
 mpool_put(pool, mem2, MPOOL_DIRTY);
    
//經(jīng)過上面的操作后mem1,mem2將不能繼續(xù)再訪問了宏悦,需要訪問時需要再次調(diào)用mpool_get。   
mem1 = (char*)mpool_get(pool, pidx1, 0);
mem2 =   (char*)mpool_get(pool, pidx2, 0);

//上面的mem1和mem2可能和前面的new返回的地址是不一樣的包吝。因此在內(nèi)存池中不能通過地址來做唯一比較饼煞,而應(yīng)該將編號來進(jìn)行比較。
       
//將所有設(shè)置為臟標(biāo)志的內(nèi)存也寫回到磁盤中去诗越。
 mpool_sync(pool);

 mpool_close(pool);  //關(guān)閉內(nèi)存池砖瞧。

 close(fd);  //關(guān)閉文件。

內(nèi)存池為iOS系統(tǒng)底層開發(fā)提供了一個非常重要的能力嚷狞,我們可以好好利用內(nèi)存池來對內(nèi)存進(jìn)行管理块促,以及一些需要進(jìn)行持久化的數(shù)據(jù)也可以借助內(nèi)存池來進(jìn)行保存,通過內(nèi)存池提高內(nèi)存的重復(fù)利用率床未。

下一篇:iOS標(biāo)準(zhǔn)庫中常用數(shù)據(jù)結(jié)構(gòu)和算法之cache


歡迎大家訪問歐陽大哥2013的github地址簡書地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末竭翠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子薇搁,更是在濱河造成了極大的恐慌斋扰,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啃洋,死亡現(xiàn)場離奇詭異传货,居然都是意外死亡,警方通過查閱死者的電腦和手機宏娄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門问裕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人孵坚,你說我怎么就攤上這事粮宛】” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵窟勃,是天一觀的道長祖乳。 經(jīng)常有香客問我,道長秉氧,這世上最難降的妖魔是什么眷昆? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮汁咏,結(jié)果婚禮上亚斋,老公的妹妹穿的比我還像新娘。我一直安慰自己攘滩,他們只是感情好帅刊,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著漂问,像睡著了一般赖瞒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚤假,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天栏饮,我揣著相機與錄音,去河邊找鬼磷仰。 笑死袍嬉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灶平。 我是一名探鬼主播伺通,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逢享!你這毒婦竟也來了呼巴?” 一聲冷哼從身側(cè)響起儡嘶,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤系吩,失蹤者是張志新(化名)和其女友劉穎吐辙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疮鲫,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡吆你,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了俊犯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妇多。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖燕侠,靈堂內(nèi)的尸體忽然破棺而出者祖,到底是詐尸還是另有隱情立莉,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布七问,位于F島的核電站蜓耻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏械巡。R本人自食惡果不足惜刹淌,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讥耗。 院中可真熱鬧有勾,春花似錦、人聲如沸古程。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挣磨。三九已至雇逞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茁裙,已是汗流浹背喝峦。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呜达,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓粟耻,卻偏偏與公主長得像查近,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挤忙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,090評論 1 32
  • 所有知識點已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)霜威? 在 Jav...
    侯蛋蛋_閱讀 2,412評論 1 4
  • InnoDB體系架構(gòu) 上圖簡單顯示了InnoDB存儲引擎的體系架構(gòu)圖中可見,InnoDB存儲引擎有多個內(nèi)存塊册烈,可以...
    Rick617閱讀 4,020評論 0 6
  • 在一個方法內(nèi)部定義的變量都存儲在棧中戈泼,當(dāng)這個函數(shù)運行結(jié)束后,其對應(yīng)的棧就會被回收赏僧,此時大猛,在其方法體中定義的變量將不...
    Y了個J閱讀 4,413評論 1 14
  • 水石軒主人閱讀 121評論 0 0