第二节 内存池(Memory Pool)
第二节 内存池(Memory Pool)
由 admin 于 周四, 2007-07-19 16:56 提交。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.
APR的大多数API都依赖于内存池(memory pool),通过使用内存池(memory pool)你可以轻松的管理一组内存空间。可以想像如果没有内存池你需要手工分配或释放内存,如果你有十个内存块,你就不得不进行十次释放操作,另外你还将面临内存泄漏的问题。内存池(memory pool)解决了这一问题,在你创建了一个内存池后,你可以从这个内存池分配出更多的内存块,需要释放这些内存时只需要销毁这个内存池即可。这样在做有两点好处,第一点使用内存池削除了内存泄漏的问题,第二点减低在非配内存时带来的损耗。从某种意义上讲,内存池强制你遵循一种面相会话(session-oriented)的方式进行编程,一个内存池是一个种会话上下文环境(session context),这样它使得一组对象拥有同一种生存周期。你可以控制在同意会话的一组对象。你可以在一个会话开始的时候创建一个内存池,然后使用这个内存创建你的对象。你不必关心他们的生存周期,在这个会话结束的时候,你通过销毁这个内存池销毁全部对象。
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.
备注:一般而言,对象的生存期控制在程序设计过程中是很复杂的,也因此出现的了很多技术相关的技术,如智能指针(smart pointer)、垃圾回收(GC, garbage collection)等等。注意!在同时使用这些技术时可能会出现一些麻烦。内存池也这些技术的一种,在混合使用其他内存回收的技术的时候需要注意。
There are three basic APIs as follows:
这里有3个基本的API:
/* 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);
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. Please take a look at mp-sample.c to know the usage.
我们可以通过使用apr_pool_create()创建一个内存池。这个内存池直到我们调用apr_pool_destroy()是才被销毁。apr_pool_create()的第一个参数是结果参数(result argument)是一个新创建的内存池对象(apr_pool_t)。我们通过调用apr_palloc(),可以获得一个指定内存大小的内存块。请参照mp-sample.c来学习这些API具体使用方法。
/* excerpted from mp-sample.c */
apr_pool_t *mp;
/* create a memory pool. */
apr_pool_create(&mp, NULL);
/* allocate memory chunks from the memory pool */
char *buf1;
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
In a nutshell, we can use apr_palloc() like malloc(3). We can also call apr_pcalloc(). As you can guess, apr_pcalloc() is similar to calloc(3). apr_pcalloc() returns a zero-cleard memory chunk. If you use malloc(3)/calloc(3), you need to call free(3) for the allocated memories. In contrast, you don't need to free each memory chunks in memory pool. You just call apr_pool_destroy() for the memory pool and it frees all the memory chunks.
简单的说,我们像使用malloc(3)一样使用apr_palloc()。我还可以调用apr_pcalloc(),正如你所预料的apr_pcalloc()类似于calloc(3),apr_pcalloc()返回一个已被清零的内存块。如果你使用malloc(3)或者calloc(3),你需要调用free(3)来释放这些已分配的内存,和他们不同的是你不需要大都为内存池中的每一个内存块进行释放操作。你仅需要调用apr_pool_destroy()来释放内存池中的全部内存块。
REMARK: There is no limitation about memory chunk size that you can allocate by apr_palloc(). Nevertheless, it isn't a good idea to allocate large size memory chunk in memory pool. That is because memory pool is essentially designed for smaller chunks. Actually, the initial size of memory pool is 8 kilo bytes. If you need a large size memory chunk, e.g. over several mega bytes, you shouldn't use memory pool.
备注:你可以使用apr_palloc()获得一个无限制的内存块,不过这并不是一个获得一个大容量内存池的好方法。内存池原本为小内存快而设计的,事实上一个内存池的初始化大小只有8k,如果你需要一个很大的内存块,比如需要一个几M字节的内存,你就不应该考虑使用内存池了。
REMARK: By default, memory pool manager never returns allocated memory back to the system. If a program runs for a long time, it would have problem. I recommend you to specify the upper limit as follows:
备注:在默认的情况下,通过内存池分配的内存是不会自动的返还给操作系统的。如果一个程序运行了很长时间,这他会出现一些问题,我推荐你在使用内存池的时候指定他的上限,代码如下:
/* sample code to set the upper limit to make memory pool manager release the memory back to the system */
#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);
}
There are two more APIs you have to know. One is apr_pool_clear(), and the other is apr_pool_cleanup_register(). apr_pool_clear() is similar to apr_pool_destroy(), but the memory pool is still reusable. A typical code is as follows:
你至少知道这个两个API的作用,一个是apr_pool_clear(),另一个是apr_pool_cleanup_register()。apr_pool_clear()和apr_pool_destroy()类似,但是使用它后这个内存是可以复用的,一个典型的代码如下:
/* 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.
这个内存池在do_operation()中被使用,在这里一些内存块被分配出来,乳沟在do_operation()之外你不需要这些内存,你可以调用apr_pool_clear(),这样你就可以减少内存使用数量。如果你熟悉栈,你可以考虑把内存池当作栈使用,通过调用apr_palloc()来移动栈的指针,通过调用apr_pool_clear()回移栈的指针,这两个操作都是轻量级的操作。
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.
通过apr_pool_cleanup_register()我们可以注册一个钩子(hook)。这个钩子在当内存池清空或者被销毁的时候被调用。在这些回调函数中,你可以实现一些回收的代码。
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. When 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.
最后一个议题是关于内存池子池的问题(sub pool),每一个内存池可以有一个父内存池。因此内存池可以构建成一个树形结构(tree),apr_pool_create()的第二个参数就是父内存池,当你使用NULL作为父内存池的时候,新创建的内存池将被编程根内存池,你可以在这个内存池下创建字内存池。当你在一个树形内存池中使用apr_pool_destroy()的时候,这个内存池的子内存池也会被销毁。当你调用apr_pool_clear()的时候,当前的内存池仍然可用,但是他的此内存池被销毁。只要子内存池的被销毁上一级的清理函数将会被调用。
REMARK: It is a typical bug that you pass NULL as pool cleanup callback function. Instead, you must pass apr_pool_cleanup_null as follows:
备注:当你相传递一个NULL作为清理的回调函数时,你必须使用apr_pool_cleanup_null代替NULL,不然会引起bug
/* pseudo code about memory pool typical bug */
/* apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, NULL); THIS IS A BUG */
/* FIXED */
apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, apr_pool_cleanup_null);
最新评论
5 天 3 小时 前
6 天 23 小时 前
6 天 23 小时 前
1 周 1 天 前
1 周 1 天 前
4 周 4 天 前
1 年 11 周 前
1 年 11 周 前
1 年 49 周 前
1 年 51 周 前