apache 中的apr_pool(内存池概览)

apr_pool是libapr库中用于内存管理的关键组件,它简化了内存块的分配和释放,防止内存泄漏。通过创建内存池,可以一次性分配多个内存块,并在结束时只需销毁内存池来释放所有内存。apr_pool提供了apr_pool_create、apr_palloc和apr_pool_destroy等基本API。使用内存池可以降低内存分配的成本,并强制实现会话导向的编程。此外,还可以通过apr_pool_cleanup_register注册清理函数,当内存池被清除或销毁时自动执行。

 

APR_POOL 帮助文档

Directory tree

1.       Brief introduction of apr_pool--------------------------------

2.       There are three basic APIs as follows:----------------------

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);

                           i.               

3.      Give a demonstration----------------------------------------------

4.       The detail-----------------------------------------------------------

5.       The contrast apr_palloc/apr_pcallo between malloc/ cal lo---------------------------------------------------------------

( Apr_pool_clearn and apr_pool_destroy------------------------- )

6.      Delegate or hook function-------------------------------------

7.   Apr_pool_cleanup_register and apr_pool_cleanup_kill-

8.       Sub pool-------------------------------------------------------------

 

Brief introduction of apr_pool

Most of libapr APIs are dependent on memory pool. By memory pool, you can easily manage a set of memory chunks. Imagine the case without memory pool system, where you allocate several memory chunks. You have to free each of them. If you have ten memory chunks, you have to free ten times, otherwise you would suffer from memory leak bugs. Memory pool solves this issue. After you allocate one memory pool, you can allocate multiple memory chunks from the pool. To free them, all you have to do is to destroy the memory pool. By which, you can free all the memory chunks. There are two good points. First, as stated above, it is defensive against memory leak bugs. Second, allocation costs of memory chunks become relatively lower. In a sense, memory pool forces you to obey a session-oriented programming. A memory pool is a kind of a session context, that is, a set of objects that have the same lifetimes. You can control a set of objects within a session context. At the beginning of a session you create a memory pool. Then, you create objects in the memory pool during the session. Note that you don't need to care about their lifetimes. Finally, at the end of the session all you have to do is to destroy the memory pool.

 

REMARK: In general, objects lifetime control is the most difficult part in programming. Thus, there are many other techniques for it, such as smart pointer, GC(garbage collection) and so on. Note that it is a bit hard to use such techniques at the same time. Since memory pool is one of such techniques, you have to be careful about the mixture.

There are three basic APIs as follows:

/* excerpted from 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);

 

Give a demonstration

 

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

 

#include <apr_general.h>

#include <apr_pools.h>

 

#define MEM_ALLOC_SIZE            1024

 

/**

  * memory pool sample code

  * @remark Error checks omitted

  */

int main(int argc, const char *argv[])

{

    apr_status_t rv;

    apr_pool_t *mp;

    char *buf1;

    char *buf2;

 

    /* per-process initialization */

    rv = apr_initialize();

    if (rv != APR_SUCCESS) {

         assert(0);

        return -1;

    }

 

    /* create a memory pool. */

    apr_pool_create(&mp, NULL);

 

    /* allocate memory chunks from the memory pool */

    buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);

    buf2 = apr_palloc(mp, MEM_ALLOC_SIZE);

 

    /* destroy the memory pool. These chunks above are freed by this */

    apr_pool_destroy(mp);

 

    apr_terminate();

    return 0;

}

The detail

/**

  * Create a new pool.

  * @param newpool The pool we have just created.

  * @param parent The parent pool.  If this is NULL, the new pool is a root

  *        pool.  If it is non-NULL, the new pool will inherit all

  *        of its parent pool's attributes, except the apr_pool_t will

  *        be a sub-pool.

  */

APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool,

                                          apr_pool_t *parent);

 

 

/*

  * Memory allocation

  */

 

/**

  * Allocate a block of memory from a pool

  * @param p The pool to allocate from

  * @param size The amount of memory to allocate

  * @return The allocated memory

  */

APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);

 

 

/**

  * Destroy the pool. This takes similar action as apr_pool_clear() and then

  * frees all the memory.

  * @param p The pool to destroy

  * @remark This will actually free the memory

  */

APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);

 

Remark

We create a memory pool by apr_pool_create(). The memory pool is alive until you call apr_pool_destroy(). The first argument of apr_pool_create() is a result argument. A newly created memory pool object, apr_pool_t, is returned by this API call. We call apr_palloc() to get a memory chunk by specifing the chunk's size. someone think it should have a function to free the memory chunk.

But,in fact, the APIs do not provide such function.That is needless. The memory chunkes that you allo-

cated through apr_palloc() will be destroyed by apr_pool_destroy(). Overview,the only way to destroy

memory chunkes is destroy the memory pool by calling the apr_pool_destroy.

   We should take notice of the rv = apr_initialize() and apr_terminate(). The apr_initialize() is setup any APR internal data structures. This must be the first function called for any APR library.See apr_app_initalize() if this is an application , rather than a library consumer of apr.You must be close apr with a call to apr_terminate at the end of program execution.

The contrast apr_palloc/apr_pcalloc between malloc/calloc

    As you can guess, we can use apr_palloc() like malloc().The different between them is that the apr_palloc() is allocate and manage memory chunk by the memory pool,but the malloc by the system.As you know, we can also use calloc().In the APIs of apr,we can use apr_pcalloc() like calloc(),apr_pcalloc() returns a zero-cleard memory chunk.If you use malloc/calloc, you need to call free() for the allocated memories.In contrast,you don’t need to free each memory chunks in memory pool.You just call apr_pool_destory() for the memory pool and it frees all the memory chunks.

   When we use apr_palloc() to allocate memory chunk,there is no limitation about memory chunk si-

ze. But it isn’t a good idea to allocate large size memory chunk in memory pool. That is because memo

ry pool is essentially designed for smaller chunks.Actually, the initial size of memory pool is 8 kilo byt

es.If you need a large size memory chunk,you should not use memory pool,but you should use malloc /

calloc.

Apr_pool_clearn and apr_pool_destroy

apr_pool_clear() is similar to apr_pool_destroy(), but the memory pool is still reusable. A typical code is as follows:

/* sample code about 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);

The memory pool is used in do_operation(), that is, several memory chunks are allocated. If you don't need the memory chunks out of do_operation(), you can call apr_pool_clear(). You are able to reduce the amount of memory usage. If you are familiar with local stack memory system, you can think of memory pool as local stack memory. Calling apr_palloc() is similar to moving SP(stack pointer), and calling apr_pool_clear() is similar to rewinding SP. Both are very light operations.

 

Delegate or hook function

Apr_pool_cleanup_register and arp_pool_cleanup_kill

By apr_pool_cleanup_register(), we can have hook functions on memory pool clear/destroy. You have a callback function that is called whenever the memory pool is cleared or destroyed. In the callback functions, you can implement any finalization code depending on the memory pool. A typical code is as follows:

….

Int *value = (int *)malloc(sizeof(int));

Apr_pool_cleanup_register(my_pool,value,free,apr_pool_cleanup_null);

Using the way,we need not take care of whether the memory chunk is free.

when apr_pool_destory(my_pool) is called,the value is automatic freed.

someone want to obvious free the vaule again. You can kill the delegate with the apr_pool_cleanup_kill(my_pool,value,free).as follow:

Int *value = (int *)malloc(sizeof(int));

Apr_pool_cleanup_register(my_pool,value,free,apr_pool_cleanup_null);

Free(value);

apr_pool_cleanup_kill(my_pool,value,free);

 

/**

  * Register a function to be called when a pool is cleared or destroyed

  * @param p The pool register the cleanup with

  * @param data The data to pass to the cleanup function.

  * @param plain_cleanup The function to call when the pool is cleared

  *                      or destroyed

  * @param child_cleanup The function to call when a child process is about

  *                      to exec - this function is called in the child, obviously!

  */

APR_DECLARE(void) apr_pool_cleanup_register(

    apr_pool_t *p,

    const void *data,

    apr_status_t (*plain_cleanup)(void *),

apr_status_t (*child_cleanup)(void *));

 

 

/**

  * Register a function to be called when a pool is cleared or destroyed.

  *

  * Unlike apr_pool_cleanup_register which register a cleanup

  * that is called AFTER all subpools are destroyed this function register

  * a function that will be called before any of the subpool is destoryed.

  *

  * @param p The pool register the cleanup with

  * @param data The data to pass to the cleanup function.

  * @param plain_cleanup The function to call when the pool is cleared

  *                      or destroyed

  */

APR_DECLARE(void) apr_pool_pre_cleanup_register(

    apr_pool_t *p,

    const void *data,

    apr_status_t (*plain_cleanup)(void *));

 

 

/**

  * Remove a previously registered cleanup function.

  *

  * The cleanup most recently registered with @a p having the same values of

  * @a data and @a cleanup will be removed.

  *

  * @param p The pool to remove the cleanup from

  * @param data The data of the registered cleanup

  * @param cleanup The function to remove from cleanup

  * @remarks For some strange reason only the plain_cleanup is handled by this

  *          function

  */

APR_DECLARE(void) apr_pool_cleanup_kill(apr_pool_t *p, const void *data,

                                        apr_status_t (*cleanup)(void *));

 

 

/**

  * Replace the child cleanup function of a previously registered cleanup.

  *

  * The cleanup most recently registered with @a p having the same values of

  * @a data and @a plain_cleanup will have the registered child cleanup

  * function replaced with @a child_cleanup.

  *

  * @param p The pool of the registered cleanup

  * @param data The data of the registered cleanup

  * @param plain_cleanup The plain cleanup function of the registered cleanup

  * @param child_cleanup The function to register as the child cleanup

  */

APR_DECLARE(void) apr_pool_child_cleanup_set(

    apr_pool_t *p,

    const void *data,

    apr_status_t (*plain_cleanup)(void *),

apr_status_t (*child_cleanup)(void *));

 

 

/**

  * An empty cleanup function.

  *

  * Passed to apr_pool_cleanup_register() when no cleanup is required.

  *

  * @param data The data to cleanup, will not be used by this function.

  */

APR_DECLARE_NONSTD(apr_status_t) apr_pool_cleanup_null(void *data);

Sub pool

The last topic about memory pool is sub pool. Each memory pool is able to have a parent memory pool. Accordingly, memory pools construct trees. The second argument of apr_pool_create() is a parent memory pool. When you pass NULL as the parent memory pool, the newly created memory pool becomes a root memory pool. You can create sub memory pools under the root memory pool. Whene you call apr_pool_destroy() for a memory pool in the tree, the child memory pools are also destroyed. When you call apr_pool_clear() for the memory pool, the memory pool is alive but the child memory pools are destroyed. Whenever a child memory pool is destroyed, the cleanup functions for it mentioned above are called.

<think>嗯,用户遇到了apr_pool_destroy无法释放内存池的问题,需要找出解决方案。首先,我得回忆一下Apache内存池APR)的基本机制。APR内存池管理是通过层级结构实现的,子内存池需要先被销毁才能释放父池的资源。可能用户没有正确销毁所有子池,导致父池无法释放。 接下来,考虑是否内存池存在循环引用。比如,如果在创建线程或其他资源时,错误地复用了同一个内存池,可能导致引用计数不为零,无法释放。这时候需要检查代码中是否有未正确分离资源的情况。 然后,检查是否有内存泄漏。比如,某些资源在创建后没有正确释放,可能影响内存池的销毁。用户提到的apr_thread使用示例中,虽然创建了线程,但未正确等待或处理线程退出,可能导致资源未被释放。这时候应该使用apr_thread_join来确保线程结束。 另外,查看APR的版本和文档是否有已知问题。旧版本可能存在bug,升级到最新版本或许能解决问题。同时,使用内存检测工具如Valgrind或APR自带的APR_POOL_DEBUG来跟踪内存分配情况,找出未释放的具体位置。 用户提供的代码示例中,主函数创建了线程池,但线程函数立即退出。可能的问题是主线程在销毁内存池前没有等待子线程完成,导致子线程仍持有池的引用。添加apr_thread_join调用应该能解决这个问题。 最后,建议用户检查内存池的使用是否符合规范,确保每次创建的子池都被正确销毁,避免跨池引用,并利用调试工具进行验证。这样综合多种可能性,逐步排查问题根源。</think>### APR内存池未释放的解决方案分析 #### 一、APR内存池机制回顾 APR内存池采用层级结构设计,父内存池销毁时会递归释放所有子内存池。但需注意以下核心特性: $$ apr\_pool\_destroy(pool) = \begin{cases} 递归释放所有子池 & \text{当无外部资源引用时} \\ 保留父池资源 & \text{当存在未释放的子池或外部引用时} \end{cases} $$ 此特性可能导致销毁失败[^1][^3] #### 二、常见问题排查路径 1. **子池未正确销毁** ```c // 错误示例 apr_pool_create(&sub_pool, parent_pool); // 创建子池 apr_pool_destroy(parent_pool); // 父池无法释放子池资源 ``` **修正方案**:需显式销毁子池或使用`apr_pool_clear()` 2. **线程未正确终止** ```c // 问题代码特征 apr_thread_create(&thread, NULL, worker, NULL, pool); apr_pool_destroy(pool); // 线程仍在运行时会阻塞销毁 ``` **解决方案**:添加线程同步机制 ```c for (int i=0; i<THREAD_NUM; i++) { apr_thread_join(&status, thread[i]); // 等待线程结束[^3] } apr_pool_destroy(pool); ``` 3. **资源引用残留** - 数据库连接未释放 - 文件描述符未关闭 - 内存池嵌套错误 #### 三、调试工具使用 1. 启用内存调试模式: ```c export APR_POOL_DEBUG=1 ``` 2. 使用Valgrind检测: ```bash valgrind --leak-check=full --track-origins=yes ./your_program ``` #### 四、典型解决方案对比 | 方案 | 适用场景 | 实现复杂度 | 效果 | |------|----------|------------|------| | `apr_pool_clear()` | 需要重用内存池 | 低 | 立即释放内存块 | | `apr_pool_destroy()` | 彻底释放场景 | 中 | 递归释放资源 | | APR_POOL_DEBUG | 调试阶段 | 高 | 精确定位泄漏点 | #### 五、推荐修复流程 1. 验证内存池层级关系 2. 检查线程同步机制 3. 使用调试模式检测 4. 添加资源释放回调 ```c apr_pool_cleanup_register(pool, NULL, your_cleanup_fn, apr_pool_cleanup_null); ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值