池销毁后未释放 APR 内存

APR memory not freed after pool destroy

apr_pool_t *pool;
char *a;
char *b;

apr_pool_create(&pool, NULL);
a = (char *) apr_palloc(pool, 10);

strcpy(a, "hello");
printf("a is %s\n", a);

apr_pool_destroy(pool);
apr_terminate();
b = (char *) apr_palloc(pool, 10);

strcpy(b, "world");
printf("b is %s\n", b);

我是 libapr 的新手,从文档中可以看出

Destroy the pool. This takes similar action as apr_pool_clear() and then frees all the memory.

但实际上不是,我仍然可以使用 apr_palloc 从被破坏的池中分配内存?所以我的问题是我怎样才能真正释放内存以及我怎样才能真正销毁池对象

您至少在两个地方调用了未定义行为。 我编译了我的 linux(Mint a derived of Ubuntu),gcc 7.4。您的程序在第一个 apr_pool_create 处崩溃,因为您没有调用适当的初始化函数(例如 apr_initialize),请参阅 https://apr.apache.org/docs/apr/1.6/group__apr__library.html。 这是 Valgrind 跟踪:

==7158== Process terminating with default action of signal 11 (SIGSEGV)
==7158==  Access not within mapped region at address 0x30
==7158==    at 0x4E595F0: apr_pool_create_ex (in /usr/lib/x86_64-linux-gnu/libapr-1.so.0.6.3)
==7158==    by 0x1088EB: main (in /home/user/apr/test)
=

一旦该问题得到解决,您将获得以下程序(注意:我不知道 apr_initializeapr_app_initialize 是否适合您的用例)。

    apr_pool_t *pool;
    char *a;
    char *b;

    apr_initialize(); // You need this or apr_app_initialize

    apr_pool_create(&pool, NULL);
    a = (char *) apr_palloc(pool, 10);

    strcpy(a, "hello");
    printf("a is %s\n", a);

    apr_pool_destroy(pool);
    apr_terminate();

    b = (char *) apr_palloc(pool, 10);

    strcpy(b, "world");
    printf("b is %s\n", b);

上面的代码在第二个 apr_palloc 中崩溃并带有以下 Valgrind 跟踪,因为您正在访问最有可能由于 apr_pool_destroy

而释放的内存
a is hello
==7196== Invalid read of size 8
==7196==    at 0x4E58A62: apr_palloc (in /usr/lib/x86_64-linux-gnu/libapr-1.so.0.6.3)
==7196==    by 0x1089AF: main (in /home/user/apr/test)
==7196==  Address 0x402d080 is not stack'd, malloc'd or (recently) free'd
==7196== 

删除最后几行

    b = (char *) apr_palloc(pool, 10);

    strcpy(b, "world");
    printf("b is %s\n", b);

允许程序正确终止并且 Valgrind 不显示任何错误。

所以看起来 apr_pool_destroy 工作正常,你只是访问了你不应该访问的内存并且你没有遇到崩溃:未定义的行为是偷偷摸摸的,你的程序可以 运行 多年来一直没有出现任何问题,然后有一天它崩溃了。

为了完整性,我使用以下命令编译(代码在test.c):

gcc -Wall test.c $(apr-1-config --cflags --cppflags --includes --link-ld) -o test

我建议您使用 Valgrind (http://www.valgrind.org/) 等工具来检测此类问题。

用户 Eliyahu Machluf(感谢)指出 APR 提供了调试内存分配的工具,来自 http://download.vikis.lt/manual/developer/debugging.html

Allocation Debugging

ALLOC_DEBUG

Debugging support: Define this to enable code which helps detect re-use of free()d memory and other such nonsense. The theory is simple. The FILL_BYTE (0xa5) is written over all malloc'd memory as we receive it, and is written over everything that we free up during a clear_pool. We check that blocks on the free list always have the FILL_BYTE in them, and we check during palloc() that the bytes still have FILL_BYTE in them. If you ever see garbage URLs or whatnot containing lots of 0xa5s then you know something used data that's been freed or uninitialized.

如前所述,apr_initialize 是使用 池的先决条件。但在使用前初始化所需数据结构的反面是在另一端将它们拆除。这就是 apr_terminate 所做的。在你拿走它所依赖的东西之后,尝试使用库进行内存分配是没有意义的。

如果你 运行 你的程序在 valgrind 下按原样(添加了初始化)然后将它与第二个版本进行比较,在第二个版本中 apr_terminate 被移动到你的结束之前代码你应该看到明显的区别。