内存池可有效降低动态申请内存的次数,减少与内核态的交互,提升系统性能,减少内存碎片,增加内存空间使用率,避免内存泄漏的可能性,这么多的优点,没有理由不在系统中使用该技术。
内存池分类:
1、 不定长内存池。典型的实现有apr_pool、obstack。优点是不需要为不同的数据类型创建不同的内存池,缺点是造成分配出的内存不能回收到池中。这是由于这种方案以session为粒度,以业务处理的层次性为设计基础。
2、 定长内存池。典型的实现有LOKI、BOOST。特点是为不同类型的数据结构分别创建内存池,需要内存的时候从相应的内存池中申请内存,优点是可以在使用完毕立即把内存归还池中,可以更为细粒度的控制内存块。
与变长的相比,这种类型的内存池更加通用,另一方面对于大量不同的数据类型环境中,会浪费不少内存。但一般系统主要的数据结构都不会很多,并且都是重复申请释放使用,这种情况下,定长内存池的这点小缺点可以忽略了。
一、apr
apr 是 apache 使用的底层库,apache 是跨平台的,其跨平台正是基于 apr。个人觉得,使用apr有两个好处,一是不用担心跨平台(事实上,我从来就不担心,因为我写的程序,从来都不跨平台)。二是 apr 的 pool 很好用。pool 有两个好处,一是可以理解成内存池,在 pool上 分配的内存,是不用释放的,pool 销毁的时候自然会释放这些内存(所以销毁(清理)pool变得异常重要,千万不能忘了)。二是可以理解成资源管理器,分配资源后,然后在pool上注册一个释放资源的函数,pool 销毁(清理)的时候,会调用这个函数,释放资源。例如打开了一个文件,可以在 pool 上注册一个关闭文件的函数,让 pool 替你关闭文件。也可以在不销毁(清理)pool 时,手动的释放。具体可以看参考apr手册。
APR的核心就是Apache的资源管理(池),我们将在本章的后面部分进行更加详细的介绍。表3-1列出了APR中的所有模块。
表3-1 APR模块
名称 | 目的 |
apr_allocator | 内存分配,内部使用 |
apr_atomic | 原子操作 |
apr_dso | 动态加载代码(.so/.dll) |
apr_env | 读取/设定环境变量 |
apr_errno | 定义错误条件和宏 |
apr_file_info | 文件系统对象和路径的属性 |
apr_file_io | 文件系统输入/输出 |
apr_fnmatch | 文件系统模式匹配 |
apr_general | 初始化/终结,有用的宏 |
名称 | 目的 |
apr_getopt | 命令参数 |
apr_global_mutex | 全局锁 |
apr_hash | 哈希表 |
apr_inherit | 文件句柄继承助手 |
apr_lib | 奇数和末端 |
apr_mmap | 内存映射 |
apr_network_io | 网络输入/输出(套接字) |
apr_poll | 投票 |
apr_pools | 资源管理 |
apr_portable | APR到本地映射转换 |
apr_proc_mutex | 进程锁 |
apr_random | 随机数 |
apr_ring | 环数据结构和宏 |
apr_shm | 共享内存 |
apr_signal | 信号处理 |
apr_strings | 字符串操作 |
apr_support | 内部支持函数 |
apr_tables | 表格和数组函数 |
apr_thread_cond | 线程条件 |
apr_thread_mutex | 线程锁 |
apr_thread_proc | 线程和进程函数 |
apr_thread_rwlock | 读写锁 |
apr_time | 时间/日期函数 |
apr_user | 用户和组ID服务 |
apr_version | APR版本 |
apr_want | 标准头文件支持 |
表3-2 APU模块
名称 | 目的 |
apr_anylock | 透明的、任何锁的封装 |
apr_base64 | Base-64编码 |
apr_buckets | Buckets/Bucket brigade |
apr_date | 时间字符串解析 |
apr_dbd | 针对SQL数据库的常用API |
apr_dbm | 针对DBM数据库的常用API |
apr_hooks | 钩子实现宏 |
apr_ldap | LDAP授权API |
apr_ldap_init | LDAP初始化API,主要应用在和LDAP服务器的初始安全连接 |
apr_ldap_option | 设置LDAP选项的API |
apr_ldap_url | 解析和处理LDAP URL的API |
apr_md4 | MD4编码 |
apr_md5 | MD5编码 |
apr_optional | 可选函数 |
apr_optional_hooks | 可选钩子 |
apr_queue | 线程安全的FIFO队列 |
apr_reslist | 资源池 |
apr_rmm | 可再定位地址的内存 |
名称 | 目的 | |
apr_sdbm | SDBM库 | |
apr_sha1 | SHA1编码 | |
apr_strmatch | 字符串模式匹配 | |
apr_uri | URI解析/构造 | |
apr_uuid | 用户标识 | |
apr_xlate | 字符集转换(I18N) | |
apr_xml | XML解析 | |
基本的约定
APR和APR-UTIL采用了一些约定,使得它们的API具有同质性,并且易于使用。
3.3.1 参考手册:API文档和Doxygen
APR和APU在代码层都有非常好的文档。每一个公开函数和数据类型都在定义它们的头文件中进行了注释,使用了doxygen 友好的格式。那些头文件,或者doxygen生成的文档,为程序员提供了完整的API参考手册。如果你安装了doxygen,那么就可以通过make dox命令从源代码中生成你自己版本的APR参考手册。
3.3.2 命名空间
所有的APR和APU的公开接口都使用了字符串前缀“apr_”(数据类型和函数)和“APR_”(宏),这就为APR定义了一个“保留”的命名