keilcjson内存分配失败_iOS标准库中常用数据结构和算法之内存池
作者丨歐陽大哥2013https://www.jianshu.com/p/34bd3e5c5b4e
??內存池
內存池提供了內存的復用和持久的存儲功能。設想一個場景,當你分配了一塊大內存并且填寫了內容,但是你又不是經常去訪問這塊內存。這樣的內存利用率將不高,而且無法復用。
而如果是采用內存池則可以很輕松解決這個問題:你只需要從內存池中申請這塊內存,設置完內容后當不需要用時你可以將這塊內存放入內存池中,供其他地方在申請時進行復用,而當你再次需要時則只需要重新申請即可。
內存池提供了內存分配編號而且設置臟標志的概念,當你把分配的內存放入內存池并設置臟標志后,系統就會在適當的時候將這塊內存的內容寫回到磁盤,這樣當你再次根據內存編號來訪問內存時,系統就又會從磁盤中將內容讀取到內存中去。
功能:在iOS中提供了一套內存池管理的API,你可以用這套API來實現上述的功能,而且系統內部很多功能也是借助內存池來實現內存復用和磁盤存儲的。
頭文件: #include
平臺: BSD系統,linux系統
一、內存池的創建、同步和關閉
功能:創建和關閉一個內存池對象并和磁盤文件綁定以便進行同步操作。函數簽名:
//創建一個內存池對象MPOOL?*?mpool_open(void?*key,?int?fd,?pgno_t?pagesize,?pgno_t?maxcache);//將內存池中的臟數據同步寫回到磁盤文件中int?mpool_sync(MPOOL?*mp);//關閉和銷毀內存池對象。int?mpool_close(MPOOL?*mp);
參數:
key:[in] 保留字段,暫時沒有用處,傳遞NULL即可。
fd:[in] 內存池關聯的磁盤文件句柄,文件句柄需要用open函數來打開。
pagesize:[in] 內存池中每次申請和分配的內存的尺寸大小,單位是字節。
maxcache:[in] ?內存池中內存頁的最大緩存數量。如果池中緩存的內存數量超過了最大緩存的數量就會復用已經存在的內存,而不是每次都分配新的內存。
return:[out] ?返回一個新創建的內存池對象,其他兩個函數成功返回0,失敗返回非0.
描述:
內存池中的內存的分配和獲取是以頁為單位進行的,每次分配的頁的尺寸大小由pagesize指定,同時內存池也指定了最大的緩存頁數量maxcache。每次從內存池中分配一頁內存時,除了會返回分配的內存地址外,還會返回這一頁內存的編號。這個編號對于內存池中的內存頁來說是唯一的。因為內存池中的內存是可以被復用的,因此就有可能是不同的編號的內存頁所得到的內存地址是相同的。
每一個內存池對象都會要和一個文件關聯起來,以便能夠實現內存數據的永久存儲和內存的復用。文件句柄必須用open函數來打開,比如下面的例子:
?int?fd?=?open("/Users/apple/mpool",?O_RDWR|O_APPEND|O_CREAT,0660);
當我們不需要使用某個內存頁時或者內存頁的內容有改動則我們需要將這個內存頁放入回內存池中,并將頁標志為臟(DIRTY)。這樣系統就會在適當的時候將此內存頁的數據寫回到磁盤文件中,同時此內存頁也會在后續被重復利用。
當我們想將所有設置為臟標志的內存頁立即寫入磁盤時則需要調用mpool_sync函數進行同步處理。
當我們不再需要內存池時,則可以通過mpool_close來關閉內存池對象,需要注意的是關閉內存池并不會將內存中的數據回寫到磁盤中去。
二、內存池中內存的獲取
功能:從內存池中申請分配一頁新的內存或者獲取現有緩存中的內存。
函數簽名:
//從內存池中申請分配一頁新的內存void?*??mpool_new(MPOOL?*mp,?pgno_t?*pgnoaddr);//根據內存編號頁獲取對應的內存。void?*?mpool_get(MPOOL?*mp,?pgno_t?pgno,?u_int?flags);
參數:
mp:[in] 內存池對象。
pgnoaddr:[out] 用于mpool_new函數,用于保存新分配的內存頁編號。
pngno:[in] ?用于mpool_get函數,指定要獲取的內存頁的編號。
flags:[in] 此參數暫時無用。
return:[out] 返回分配或者獲取的內存地址。如果分配或者獲取失敗則返回NULL。
描述:
無論是new還是get每次從內存池里面分配或者獲取的內存頁的大小都是由上述mpool_open函數中的pagesize參數指定的大小。
系統內部分配的內存是用calloc函數實現的,但是我們不需要手動調用free來對內存進行釋放處理。
每個內存頁都有一個唯一的頁編號,而且每次分配的頁編號也會一直遞增下去。
mpool_new函數申請分配新的內存時,如果當前緩存中的內存頁小于maxcache數量則總是分配新的內存,只有當緩存數量大于maxcache時才會從現有的緩存中尋找一頁可以被重復利用的內存頁,如果沒有可以重復利用的頁面,則會繼續分配新的內存頁。
mpool_get函數則根據內存頁的編號獲取對應的內存頁。如果編號不存在則返回NULL。需要注意的是一般在獲取了某一頁內存后,不要進行重復獲取操作,否則在DEBUG狀態下會返回異常。另外一個情況是有可能相同的頁編號下兩次獲取的內存地址是不一樣的,因為系統實現內部有內存復用的機制。
三、內存池中內存的放回
功能:將分配或者申請的內存頁放回到內存池中去,以便進行重復利用。函數簽名:
int??mpool_put(MPOOL?*mp,?void?*pgaddr,?u_int?flags);
參數:
mp: [in] 內存池對象。
pgaddr:[in] 要放入緩存的內存頁地址。這個地址由mpool_get/new兩個函數返回。
flags:[in] 放回的屬性,一般設置為0或者MPOOL_DIRTY。
return:[in] 函數調用成功返回0,失敗返回非0
描述:
這個函數用來將內存頁放入回內存池緩存中,以便對內存進行重復利用。當將某個內存地址放入回緩存后,將不能再次訪問這個內存地址了。如果要想繼續訪問內存中的數據則需要借助上述的mpool_get/new函數來重新獲取。
flags:屬性如果指定為0時,表明放棄這次內存中的內容的修改,系統不會將內存中的內容寫入到磁盤中,而只是將內存放入緩存中供其他地方重復使用。而如果設置為MPOOL_DIRTY時,則表明將這頁內存中的數據設置為臟標志,除了同樣將內存放入緩存中重復利用外,則會在適當的時候將內存中的數據寫入到磁盤中,以便下次進行讀取。
四、內存池磁盤讀寫通知
功能:注冊回調函數,當某頁內存要寫回到磁盤或者要從磁盤中讀取時就會調用指定的回調函數。
函數簽名:
void?mpool_filter(MPOOL?*mp,?void?(*pgin)(void?*,?pgno_t,?void?*),
?????????void?(*pgout)(void?*,?pgno_t,?void?*),?void?*pgcookie);
參數:
mp:[in] 內存池對象.
pgin: [in]: 回調函數,當某個內存頁的數據需要從磁盤讀取時,會在讀取完成后調用這個回調函數。
pgout:[in]: 回調函數,當某個內存頁的數據要到磁盤時,會在寫入完成后調用這個回調函數。
pgcookie: [in] 上述兩個回調函數的附加參數。
描述:
因為內存池中的內存頁會進行復用,以及會在適當的時候將內容同步到磁盤中,或者從磁盤中將內容讀取到內存中,因此可以借助這個函數來監控這些磁盤文件和內存之間的讀寫操作。pgin和pgout函數的格式定義如下:
//pgin和pgout回調函數的格式。//pgcookie:是mpool_filter函數中傳入的參數。//pgno:?要進行讀寫的內存頁編號//pageaddr:?要進行讀寫的內存地址。void?(*pgcallback)(void?*pgcookie,?pgno_t?pgno,?void?*pageaddr);
五、實例代碼
//創建并打開一個文件。int?fd?=?open("/Users/apple/mpool",?O_RDWR|O_APPEND|O_CREAT,0660);//創建一個內存池對象,每頁的內存100個字節,最大的緩存數量為4
?MPOOL?*pool?=?mpool_open(NULL,?fd,?100,?4);//從內存池中分配一個新的內存頁,這里對返回的內存填寫數據。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);//將分配的內存mem1放回內存池中,但是內容不保存到磁盤
?mpool_put(pool,?mem1,?0);//將分配的內存mem2放回內存池中,但是內容保存到磁盤。
?mpool_put(pool,?mem2,?MPOOL_DIRTY);//經過上面的操作后mem1,mem2將不能繼續再訪問了,需要訪問時需要再次調用mpool_get。
mem1?=?(char*)mpool_get(pool,?pidx1,?0);
mem2?=???(char*)mpool_get(pool,?pidx2,?0);//上面的mem1和mem2可能和前面的new返回的地址是不一樣的。因此在內存池中不能通過地址來做唯一比較,而應該將編號來進行比較。//將所有設置為臟標志的內存也寫回到磁盤中去。
?mpool_sync(pool);
?mpool_close(pool);??//關閉內存池。
?close(fd);??//關閉文件。
內存池為iOS系統底層開發提供了一個非常重要的能力,我們可以好好利用內存池來對內存進行管理,以及一些需要進行持久化的數據也可以借助內存池來進行保存,通過內存池提高內存的重復利用率。
?推薦↓↓↓?
長
按
關
注
?【16個技術公眾號】都在這里!
涵蓋:程序員大咖、源碼共讀、程序員共讀、數據結構與算法、黑客技術和網絡安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、數據庫研發、幽默程序員等。
萬水千山總是情,點個 “在看” 行不行總結
以上是生活随笔為你收集整理的keilcjson内存分配失败_iOS标准库中常用数据结构和算法之内存池的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中段尾段全段什么意思_头段?中尾段?还是
- 下一篇: vts传感器采取船舶的_【火炬高企】船舶