apr_pool -- 内存池
生活随笔
收集整理的這篇文章主要介紹了
apr_pool -- 内存池
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這個指南主要介紹如何使用
libapr
(
apache portable runtime
)。
版權所有, Copyright (C) 2005 INOUE Seiichiro <inoue&ariel-networks.com> ,翻譯:成彥
原文地址: http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial.html
轉載時請保留版權信息。
大多數 libapr 的 API 都依賴于內存池,借助內存池,簡化了內存塊的管理。想像一下沒有內存池系統的情況:你申請一些內存塊就必需逐個釋放它們,例如如果你申請了 10 個內存塊,你必需釋放 10 次,否則,你將遭受內存泄露的錯誤。內存池解決了這個令人感到繁瑣的問題,在申請一個內存池之后,你可以從內存池中申請多個內存塊,釋放它們的時候,你所需要做的就是銷毀內存池,這樣你就可以釋放所有的內存塊了。這有兩個優點,第一,它可以預防內存泄露的錯誤;第二,分配內存塊的開銷相對變低了。從某種意義上說,內存池迫使你遵循面向會話編程,一個內存池就是一種會話內容,這樣,處于同一個內存池中的對象就有相同的生命周期,你可以通過控制會話內容來控制對象。在一個會話的開始,你創建了一個內存池,接著,你在內存池中創建了一些對象,你不需要去關心這些對象的生命周期,最后,在會話結束的時候,你只需要將那個內存池銷毀就可以了。
注:通常,對象生命周期控制是程序開發最困難的部分,因此,針對這個問題還存在有一些技術,例如智能指針,垃圾回收機制等等。需要注意,同時使用這些技術有一定的難度,內存池也是這其中的一項技術,所以你不得不非常小心的使用它們。
注:在將來, libapr 的內存池將變得不再那么重要。參見 http://mail-archives.apache.org/mod_mbox/apr-dev/200502.mbox/%3c1f1d9820502241330123f955f@mail.gmail.com%3e.
下面有三個基本的 API 函數 :
/*? 摘自 ?apr_pools.h */
??? APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool, apr_pool_t *parent);
??? APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);
??? APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);
我們使用 apr_pool_create() 函數創建一個內存池,這個內存池將一直存活,直到你調用 apr_pool_destroy() 函數以后被銷毀。 apr_pool_create() 的第一個參數是一個結果輸出參數,是一個新創建的 apr_pool_t 類型的內存池對象。通過調用 apr_palloc() 來申請指定大小的內存塊,具體使用方法見 mp-sample.c 。
/*? 摘自 ?mp-sample.c */
??? apr_pool_t *mp;
??? /*? 創建內存池 ?*/
??? apr_pool_create(&mp, NULL);
??? /*? 從內存池中分配內存塊 ?*/
??? char *buf1;
??? buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
簡單地說,我們可以像使用 malloc(3) 這樣使用 apr_palloc() ,也可以調用 apr_pcalloc() ,正如你猜到的, apr_pcalloc 類似于 calloc(3) , apr_pcalloc 返回一個被 0 填充了的內存塊。假如你使用了 malloc(3)/calloc(3) ,你需要調用 free(3) 來釋放分配了的內存。但是在內存池中,你必不需要釋放每個內存塊,你只需要對該內存池調用 apr_poll_destroy() 函數從而釋放所有的內存塊。
注:使用 apr_palloc() 申請內存,其內存塊的大小沒有限制,然而,在內存池中申請大內存并不是什么好主意。內存池本質上是為了更小的內存塊而設計的,實際上,初始的內存池的大小是 8000 字節。如果你需要申請超過幾兆字節的內存塊時,那么就不要使用內存池。
注:默認情況下,內存池管理器從不將申請到的內存歸還給系統。如果程序要運行很長時間,這將是一個問題,推薦像下面的代碼那樣指定一個上限:
/*? 設置上限,讓內存池管理器釋放內存,將內存返回給系統的示例代碼 ?*/
#define YOUR_POOL_MAX_FREE_SIZE 32????? /* apr_pool max free list size */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
apr_allocator_t* pa = apr_pool_allocator_get(mp);
if (pa) {
??? apr_allocator_max_free_set(pa, YOUR_POOL_MAX_FREE_SIZE);
}
這兒有兩個 API 函數需要知道,一個是 apr_pool_clear() ,另一個是 apr_pool_cleanup_register() , apr_pool_clear() 類似于 apr_pool_destroy() ,不同的是內存池將一直存在。示例代碼如下:
/*? 使用 apr_pool_clear() 的例子 ?*/
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
??? do_operation(..., mp);
??? apr_pool_clear(mp);
}
apr_pool_destroy(mp);
do_operation() 里使用了內存池,分配了一些內存塊。假如在 do_operation() 之外不需要這些內存塊了,可以調用 apr_pool_clear() 函數,這樣能縮小內存的使用大小。如果你熟悉系統的棧內存的話,你會覺得內存池與棧內存一樣,調用 apr_palloc 只是如同移動 SP (棧指針),調用 apr_pool_clear() 如同重置 SP ,兩者都是輕量級的操作。
使用 apr_pool_cleanup_register() 函數,可以在內存池清空 / 銷毀上設定一個鉤子(回調)函數,在內存池清空或是銷毀后調用這個函數,在這個回調函數中,你可以實現任何在內存池上的結束代碼。
關于內存池的最后一個主題是子池,每個內存池都可以有一個父內存池,因此,內存池構造了樹。 apr_pool_create() 的第二個參數表示父內存池,當這個參數為 NULL 時,新創建的內存池將變為一個根內存池,可以在這個根內存池上創建子內存池。在這個樹中對一個內存池調用 apr_pool_destroy() 函數,則該內存池的子內存池也將被銷毀;當對該內存池調用 apr_pool_clear() 函數,則這個內存池存在但是它的子內存池將被銷毀,上面提及到的那些清除函數,在子內存池銷毀時被調用。
注:當將 NULL 值做為清除回調函數時將會產生一個 bug ,你必須像下面的代碼那樣傳入 apr_pool_cleanup_null :
/*? 關于內存池典型 bug 的偽代碼 ?*/
/* apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, NULL);? 這將產生一個 bug */
/*? 修正: ?*/
版權所有, Copyright (C) 2005 INOUE Seiichiro <inoue&ariel-networks.com> ,翻譯:成彥
原文地址: http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial.html
轉載時請保留版權信息。
大多數 libapr 的 API 都依賴于內存池,借助內存池,簡化了內存塊的管理。想像一下沒有內存池系統的情況:你申請一些內存塊就必需逐個釋放它們,例如如果你申請了 10 個內存塊,你必需釋放 10 次,否則,你將遭受內存泄露的錯誤。內存池解決了這個令人感到繁瑣的問題,在申請一個內存池之后,你可以從內存池中申請多個內存塊,釋放它們的時候,你所需要做的就是銷毀內存池,這樣你就可以釋放所有的內存塊了。這有兩個優點,第一,它可以預防內存泄露的錯誤;第二,分配內存塊的開銷相對變低了。從某種意義上說,內存池迫使你遵循面向會話編程,一個內存池就是一種會話內容,這樣,處于同一個內存池中的對象就有相同的生命周期,你可以通過控制會話內容來控制對象。在一個會話的開始,你創建了一個內存池,接著,你在內存池中創建了一些對象,你不需要去關心這些對象的生命周期,最后,在會話結束的時候,你只需要將那個內存池銷毀就可以了。
注:通常,對象生命周期控制是程序開發最困難的部分,因此,針對這個問題還存在有一些技術,例如智能指針,垃圾回收機制等等。需要注意,同時使用這些技術有一定的難度,內存池也是這其中的一項技術,所以你不得不非常小心的使用它們。
注:在將來, libapr 的內存池將變得不再那么重要。參見 http://mail-archives.apache.org/mod_mbox/apr-dev/200502.mbox/%3c1f1d9820502241330123f955f@mail.gmail.com%3e.
下面有三個基本的 API 函數 :
/*? 摘自 ?apr_pools.h */
??? APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool, apr_pool_t *parent);
??? APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);
??? APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);
我們使用 apr_pool_create() 函數創建一個內存池,這個內存池將一直存活,直到你調用 apr_pool_destroy() 函數以后被銷毀。 apr_pool_create() 的第一個參數是一個結果輸出參數,是一個新創建的 apr_pool_t 類型的內存池對象。通過調用 apr_palloc() 來申請指定大小的內存塊,具體使用方法見 mp-sample.c 。
/*? 摘自 ?mp-sample.c */
??? apr_pool_t *mp;
??? /*? 創建內存池 ?*/
??? apr_pool_create(&mp, NULL);
??? /*? 從內存池中分配內存塊 ?*/
??? char *buf1;
??? buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
簡單地說,我們可以像使用 malloc(3) 這樣使用 apr_palloc() ,也可以調用 apr_pcalloc() ,正如你猜到的, apr_pcalloc 類似于 calloc(3) , apr_pcalloc 返回一個被 0 填充了的內存塊。假如你使用了 malloc(3)/calloc(3) ,你需要調用 free(3) 來釋放分配了的內存。但是在內存池中,你必不需要釋放每個內存塊,你只需要對該內存池調用 apr_poll_destroy() 函數從而釋放所有的內存塊。
注:使用 apr_palloc() 申請內存,其內存塊的大小沒有限制,然而,在內存池中申請大內存并不是什么好主意。內存池本質上是為了更小的內存塊而設計的,實際上,初始的內存池的大小是 8000 字節。如果你需要申請超過幾兆字節的內存塊時,那么就不要使用內存池。
注:默認情況下,內存池管理器從不將申請到的內存歸還給系統。如果程序要運行很長時間,這將是一個問題,推薦像下面的代碼那樣指定一個上限:
/*? 設置上限,讓內存池管理器釋放內存,將內存返回給系統的示例代碼 ?*/
#define YOUR_POOL_MAX_FREE_SIZE 32????? /* apr_pool max free list size */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
apr_allocator_t* pa = apr_pool_allocator_get(mp);
if (pa) {
??? apr_allocator_max_free_set(pa, YOUR_POOL_MAX_FREE_SIZE);
}
這兒有兩個 API 函數需要知道,一個是 apr_pool_clear() ,另一個是 apr_pool_cleanup_register() , apr_pool_clear() 類似于 apr_pool_destroy() ,不同的是內存池將一直存在。示例代碼如下:
/*? 使用 apr_pool_clear() 的例子 ?*/
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
??? do_operation(..., mp);
??? apr_pool_clear(mp);
}
apr_pool_destroy(mp);
do_operation() 里使用了內存池,分配了一些內存塊。假如在 do_operation() 之外不需要這些內存塊了,可以調用 apr_pool_clear() 函數,這樣能縮小內存的使用大小。如果你熟悉系統的棧內存的話,你會覺得內存池與棧內存一樣,調用 apr_palloc 只是如同移動 SP (棧指針),調用 apr_pool_clear() 如同重置 SP ,兩者都是輕量級的操作。
使用 apr_pool_cleanup_register() 函數,可以在內存池清空 / 銷毀上設定一個鉤子(回調)函數,在內存池清空或是銷毀后調用這個函數,在這個回調函數中,你可以實現任何在內存池上的結束代碼。
關于內存池的最后一個主題是子池,每個內存池都可以有一個父內存池,因此,內存池構造了樹。 apr_pool_create() 的第二個參數表示父內存池,當這個參數為 NULL 時,新創建的內存池將變為一個根內存池,可以在這個根內存池上創建子內存池。在這個樹中對一個內存池調用 apr_pool_destroy() 函數,則該內存池的子內存池也將被銷毀;當對該內存池調用 apr_pool_clear() 函數,則這個內存池存在但是它的子內存池將被銷毀,上面提及到的那些清除函數,在子內存池銷毀時被調用。
注:當將 NULL 值做為清除回調函數時將會產生一個 bug ,你必須像下面的代碼那樣傳入 apr_pool_cleanup_null :
/*? 關于內存池典型 bug 的偽代碼 ?*/
/* apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, NULL);? 這將產生一個 bug */
/*? 修正: ?*/
apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, apr_pool_cleanup_null);
一個簡單的示例:
注意編譯時候加上:
LIBS := -lactivemq-cpp -lvoltdbcpp -lpq
//============================================================================ // Name : tt.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================#include <stdio.h> #include <string.h> #include <iostream> #include <vector> #include <boost/unordered_map.hpp> #include <boost/threadpool.hpp> #include<apr_pools.h>using namespace std;class APRMemoryPool { public:APRMemoryPool();virtual ~APRMemoryPool();public:static APRMemoryPool* get_instance();long init();long uninit();void* malloc(boost::thread::id threadid,size_t alloc_size);void free(boost::thread::id threadid);protected:long alloc_new_pool(boost::thread::id threadid, apr_pool_t** new_alloc_pool);long release_unused_pool(apr_pool_t** release_pool);private:apr_pool_t* root_pool;vector<apr_pool_t*> recycled_apr_pools;boost::unordered_map< boost::thread::id, apr_pool_t* > using_pools;boost::mutex pools_mutex_map;boost::mutex pools_mutex_vector; }; APRMemoryPool gl_APRMemoryMgr;APRMemoryPool* APRMemoryPool::get_instance(){return &gl_APRMemoryMgr; }APRMemoryPool::APRMemoryPool() :root_pool(NULL){ }APRMemoryPool::~APRMemoryPool(){uninit(); }long APRMemoryPool::init(){apr_pool_initialize();apr_pool_create(&root_pool,NULL);return 0; }long APRMemoryPool::uninit(){apr_pool_destroy(root_pool);apr_pool_terminate();return 0; }void* APRMemoryPool::malloc(boost::thread::id threadid, size_t alloc_size){apr_pool_t *thread_memory_pool = NULL;{boost::unique_lock<boost::mutex> malloc_lock(pools_mutex_map);boost::unordered_map<boost::thread::id, apr_pool_t*>::iterator iter = using_pools.find(threadid);if(iter != using_pools.end()){thread_memory_pool = iter->second;return apr_palloc(thread_memory_pool,alloc_size);}else{if(recycled_apr_pools.size()!=0){thread_memory_pool = recycled_apr_pools.back();recycled_apr_pools.pop_back();using_pools.insert(make_pair(threadid,thread_memory_pool));return apr_palloc(thread_memory_pool,alloc_size);}else{apr_pool_create(&thread_memory_pool,root_pool);using_pools.insert(make_pair(threadid,thread_memory_pool));cout<<"apr pool create a new one..............."<<endl;return apr_palloc(thread_memory_pool,alloc_size);}}} }void APRMemoryPool::free(boost::thread::id threadid) {apr_pool_t *release_pool = NULL;{boost::unique_lock<boost::mutex> free_lock(pools_mutex_map);boost::unordered_map<boost::thread::id,apr_pool_t*>::iterator iter = using_pools.find(threadid);if(iter != using_pools.end()){release_pool = iter->second;apr_pool_clear(release_pool);using_pools.erase(threadid);recycled_apr_pools.push_back(release_pool);}else{cout<<"unexcepted error....."<<endl;}} }long APRMemoryPool::alloc_new_pool(boost::thread::id threadid, apr_pool_t** alloc_new_pool) {return 0; }int main(int argc, char *argv[]) {APRMemoryPool::get_instance()->init();void *p=APRMemoryPool::get_instance()->malloc(boost::this_thread::get_id(),100);std::cout <<p <<std::endl;APRMemoryPool::get_instance()->init();return (0); }
總結
以上是生活随笔為你收集整理的apr_pool -- 内存池的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单使用Boost线程池threadpo
- 下一篇: gdb调试fork多进程