Apache APR memory pool 的一点见解

本文探讨了APR Memory Pool的非线程安全特性及其实现策略,介绍了如何通过子内存池减少内存碎片并提高效率,同时对比了Memory Pool与Object Pool在不同场景下的适用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

APR 是 Apache Portable Runtime project 的简称,对于 APR 是否值得作为日常开发工具箱里面的一员,我不做太多的评价,对于 APR 1.x 过分依赖 memory pool 的讨论也很多,各位自行去实践。

对于 APR,我并没有太多关注它的 portable,日常用的较多的是它的 memory pool 实现。memory pool 可以帮助我们在有限的开销下简化对内存块申请释放的管理,让我们的代码更优雅,更易维护。另外对于 24 X 7 长时间不间断运行的服务器程序来说,海量 malloc/free 造成内存碎片也是通常大家需要面对的问题。当然这个改进的办法有很多,我自己实践过的有下面几种,实现自己的内存申请释放管理机制(内存池),重用 struct 或者 object,对每次 malloc 的内存块大小对齐等等。就目前而言,我觉得 apr memory pool + struct/object 重用已经能满足上述两个需求。下面说说我对 apr memory pool 的一点见解。


1, apr memory pool 大多操作都是非线程安全,任何时候只能有一个线程对同一个 pool 进行操作,除了一个例外(APR 1.4.5)。

 * Note that most operations on pools are not thread-safe: a single pool
 * should only be accessed by a single thread at any given time. The one
 * exception to this rule is creating a subpool of a given pool: one or more
 * threads can safely create subpools at the same time that another thread
 * accesses the parent pool.
 */


    针对这点,很直接的思考就是用 “锁” 去保护。我个人的倾向或者说喜好是 lock free。过多的“锁”必然降低目前多核机器下程序的并发度,也对程序并发架构的优化带来其他问题。针对这点,我在使用 apr memory pool 的时候,通常不引入另外的“锁”,而是和 data context 关联在一起,让 data 的“锁”去间接保护。

typedef struct
{
apr_pool_t *pool;
MyMutex *data_mutex;
void *data;
} data_context_t;

2, 我会适当使用 subpool,这个也得益于 apr memory pool 在创建 subpool 的时候是线程安全的。

在同一个 app context 下,会创建一个属于该 app context 的 memory pool,而从属于该 app context 的 data 的 memory pool 就会是它的 subpool。另外在对临时内存用量要求较多的函数或者循环代码都会生成 subpool,在使用完毕后释放。

void func_require_large_tmp_mem(apr_pool_t *parent_pool, ...)
{
apr_pool_t *subpool;
apr_pool_create(&subpool, parent_pool);
...
apr_pool_destroy(subpool);
}

apr_pool_t *subpool;
apr_pool_create(&subpool, parent_pool);
while (cond)
{
.... // large tmp mem require
}
apr_pool_destroy(subpool);

3, memory pool 不是万能的,某些条件下使用 object pool 也是不错的选择

memory pool 在内存不足的情况下需要从 OS 额外获取。这个通常发生在该 memory pool 初始内存大小或者剩余可用内存大小小于外部需求的时候。如果一个 app,不同 object 使用的 mem 差异是较大的,那么会造成要么 memory pool 浪费太多(APR MIN_ALLOC 是 8192),或者 memory pool 需要频繁的扩大。要解决这个问题,一个方式是在创建 memory pool 的时候根据程序上下文以及所需要处理的数据指定 memory pool 预先准备的可用内存大小以及后续扩大可用内存的大小。在这个时候,object pool 也许就应该作为我们的一个考虑。每个类型的 object 因为处理同一类的数据或者消息,因此重用该 object,可以让 memory pool 释放空余内存或者申请额外可用内存的操作大大降低。同时也能一定程度上避开 malloc/free 等内存管理在多核系统里面的并发瓶颈(当然我们可以 PRELOAD 外部的 malloc/free 实现去替换原有的)。 

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值