这个指南主要介绍如何使用 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 */
/*  修正:  */

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 -- 内存池相关推荐

  1. 不定长内存池之apr_pool

    内存池可有效降低动态申请内存的次数,减少与内核态的交互,提升系统性能,减少内存碎片,增加内存空间使用率,避免内存泄漏的可能性,这么多的优点,没有理由不在系统中使用该技术. 内存池分类: 1.      ...

  2. C++性能优化(七)——内存池技术

    一.内存池简介 1.C++内存池简介 内存池(Memory Pool)是一种内存分配方式,是在真正使用内存前,先申请分配一定数量的.大小相等(一般情况下)的内存块留作备用.当有新的内存需求时,就从内存 ...

  3. linux内存分配 连续 足够,linux内存池能分配连续物理内存吗

    中. size参数: 内核是基于页技术分配内存,以最佳的利用系统的RAM. linux处理内存分配的方法是:创建一系列的内存对象池,每个池的内存大小事固定的,处理分配请求时,就直接在包含足够大的内存块 ...

  4. ceph bluestore源码分析:admin_socket实时获取内存池数据

    环境: 版本:ceph 12.2.1 部署完cephfs 使用ceph-fuse挂载,并写入数据 关键参数: debug_mempool = true 将该参数置为true即可查看详细的blustor ...

  5. Memcached内存池分析

    针对Memcacged1.4.15代码 1.完整slabs内存池图 这是我画的memcached的slabs内存池对象关系图: 2.内存池数据结构 typedef struct {unsigned i ...

  6. 提高C++性能的编程技术笔记:多线程内存池+测试代码

    为了使多个线程并发地分配和释放内存,必须在分配器方法中添加互斥锁. 全局内存管理器(通过new()和delete()实现)是通用的,因此它的开销也非常大. 因为单线程内存管理器要比多线程内存管理器快的 ...

  7. 提高C++性能的编程技术笔记:单线程内存池+测试代码

    频繁地分配和回收内存会严重地降低程序的性能.性能降低的原因在于默认的内存管理是通用的.应用程序可能会以某种特定的方式使用内存,并且为不需要的功能付出性能上的代价.通过开发专用的内存管理器可以解决这个问 ...

  8. 某内存池中的指针用法

    内存池实现有许多种,各有不同的优缺点. 这里不是主要说内存池,只是觉得这个内存池中的指针用得很飘逸! template <class T,int AllocSize = 50> class ...

  9. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

最新文章

  1. bing浏览器_Bing正式改名Microsoft Bing:不再只是搜索
  2. 让你的数据离CPU更近一些
  3. 面向对象的三大特性(封装、继承、多态)
  4. sql 取表的前10条记录,任意中间几行的记录
  5. 技术正文 history命令添加时间---测试磁盘写入速度
  6. PHP设计模式之装饰者模式
  7. linux 的 usr 文件
  8. csm和uefi_关于CSM和UEFI你要知道的一些事
  9. 爱奇艺怎么看不了电视剧和视频
  10. 用 #inculde file = ../fiel 报1031错误
  11. 如何将苹方字体写入html,html苹方字体
  12. 万维网联盟W3C发布HTML5新logo
  13. 转载HTML实体字符
  14. 拉格朗日乘子法和KTT条件
  15. 莫纳什计算机专业强吗,莫纳什大学计算机专业怎么样
  16. 金仓数据库KingbaseES使用ksql连接认证失败
  17. 无线路由器连接电信光猫实现拨号上网方法
  18. vue中将字符转换成数字的简单做法
  19. 《沉默的大多数》王小波——读后感
  20. 文本匹配与ESIM模型详解

热门文章

  1. gateway网关配置入门
  2. 选择结构_标准的switch语句
  3. flume高可用-balance-配置文件编写
  4. Response_案例2_输出字符数据
  5. java.util.function包
  6. LinkedBlockingDeque源码
  7. 将 C++ 中的 goto 语句转化为非 goto 语句
  8. Testlink使用介绍
  9. 《Spark大数据分析:核心概念、技术及实践》一3.5 API
  10. windows 2008创建群集“xxx”时出错。由于超时时间已过,该操作返回