Apache 中使用的 APR allocator分析
[结构体]
//内存分配器结构
struct apr_allocator_t {
apr_uint32_t max_index; //空闲队列中,最大的内存块级数
apr_uint32_t max_free_index; //直接回收到系统的内存大小,基数是4096
apr_uint32_t current_free_index;//动态回收到系统的内存大小,是具体的控制,基数是4096,超过后对新回收的内存块,直接回收到系统
apr_thread_mutex_t *mutex; //互拆体
apr_pool_t *owner; //所有都
apr_memnode_t *free[MAX_INDEX];//空闲内存块的链表,分级链表
};
将内存按大小分级存在free列表中,总共分为MAX_INDEX(20)级,
内存的最小块是8192,在free[1]中,以后每级增加4096字节,1-19级每级内的所有块的大小都是一样,
超过19级的都存到free[0]中,free[0]中的大小不是固定的
//单个内存块/内存结点的结构
struct apr_memnode_t {
apr_memnode_t *next; /**< next memnode */
apr_memnode_t **ref; /**< reference to self */
apr_uint32_t index; /**< size */
apr_uint32_t free_index; /**< how much free */
char *first_avail; /**< pointer to first free memory */
char *endp; /**< pointer to end of free memory */
};
[函数]
创建一个内存分配器,创建时并没有分配内存块
apr_status_t apr_allocator_create (apr_allocator_t **allocator)
销毁一个已经存在的内存分配器,销毁时,先释放里面的空闲内存块
void apr_allocator_destroy (apr_allocator_t *allocator)
从内存分配器中分配一块内存块
apr_memnode_t* apr_allocator_alloc (apr_allocator_t *allocator, apr_size_t size)
回收一块内存块或一链表内存块回内存分配器
void apr_allocator_free (apr_allocator_t *allocator, apr_memnode_t *memnode)
设置所有者,只是用来标识的,不设也没事
void apr_allocator_owner_set (apr_allocator_t *allocator, apr_pool_t *pool)
获取所有者
apr_pool_t * apr_allocator_owner_get (apr_allocator_t *allocator)
设置直接回收到系统的内存块大小,超过的内存块在回收时直接释放到系统,而不是存到内存分配器
void apr_allocator_max_free_set (apr_allocator_t *allocator, apr_size_t size)
设置互拆体,多线程时一定要用到
void apr_allocator_mutex_set (apr_allocator_t *allocator, apr_thread_mutex_t *mutex)
获取内存分配器中使用的互拆体
apr_thread_mutex_t * apr_allocator_mutex_get (apr_allocator_t *allocator)
主要是两个结构的理解,和内存的分配的回收
内存分配
1.按存需求的内存块大小,先用4096对齐,再确定对应的级数index.
/* Round up the block size to the next boundary, but always
* allocate at least a certain size (MIN_ALLOC).
*/
size = APR_ALIGN(size + APR_MEMNODE_T_SIZE, BOUNDARY_SIZE);
if (size < MIN_ALLOC)
size = MIN_ALLOC;
/* Find the index for this node size by
* dividing its size by the boundary size
*/
index = (size >> BOUNDARY_INDEX) - 1;
if (index > APR_UINT32_MAX) {
return NULL;
}
2.如果当前的级数(index)没有超过队列中已有的最大级数(max_index) index <= allocator->max_index,按最小适配原则,从低级往高级,找到空闲内存块返回
max_index = allocator->max_index;
ref = &allocator->free[index];
i = index;
while (*ref == NULL && i < max_index) {
ref++;
i++;
}
如果是最大的级数,且是最后一个节点,则调整max_index
if ((*ref = node->next) == NULL && i >= max_index) {
do {
ref--;
max_index--;
}
while (*ref == NULL && max_index > 0);
allocator->max_index = max_index;
}
调大current_free_index 的大小,增大回收的门槛. 但最大不能超过max_free_index
allocator->current_free_index += node->index;
if (allocator->current_free_index > allocator->max_free_index)
allocator->current_free_index = allocator->max_free_index;
3.如果当前的级数(index)超过队列中已有的最大级数(max_index),且free[0]队列不为空,则到free[0]中去查找有无合适的块,找到就返回
while ((node = *ref) != NULL && index > node->index)
ref = &node->next;
也调大current_free_index 的大小,增大回收的门槛. 但最大不能超过max_free_index
4.如果上面的2,3都没有找到,说明没有够到的空闲块,则在物理内存中分配新块.
if ((node = malloc(size)) == NULL)
return NULL;
node->next = NULL;
node->index = (APR_UINT32_TRUNC_CAST)index;
node->first_avail = (char *)node + APR_MEMNODE_T_SIZE;
node->endp = (char *)node + size;
内存回收
这里的内存回收,如果存进来有后续结点,则后续结点也一起回收,即回收的是内存队列
1.设置了回收的内存大小,且当前回收的内存块的级数index大于当前的回收级数current_free_index,则直接放到释放队列freelist中
if (max_free_index != APR_ALLOCATOR_MAX_FREE_UNLIMITED
&& index > current_free_index) {
node->next = freelist;
freelist = node;
}
2.不符合回收到系统的,就回收到相应的队列中
if (index < MAX_INDEX) {
/* Add the node to the appropiate 'size' bucket. Adjust
* the max_index when appropiate.
*/
if ((node->next = allocator->free[index]) == NULL
&& index > max_index) {
max_index = index;
}
allocator->free[index] = node;
current_free_index -= index;
}
else {
/* This node is too large to keep in a specific size bucket,
* just add it to the sink (at index 0).
*/
node->next = allocator->free[0];
allocator->free[0] = node;
current_free_index -= index;
}
回收后,调整current_free_index 的大小,降低回收的门槛.
3.如果有后续结点,则接着处理,直到结束
4.全部处理完后,释放freelist中的节点给系统
这里面最难理解的是current_free_index ,是一个动态调整的值,跟据分配内存的情况,分配时,增加current_free_index的大小,但最大不会超过设置的max_free_index值
回收时,只要有回收到队列中,就降低current_free_index的大小,这样就保证队列中大节点的内存块不会太多.也不会出现有某级结点空的情况
我感觉存在几个问题:
1.每块最小8K好像太大了,如果我是处理音频,压缩后一般就60-150字节,或是网络包,UDP最大才1.4K,那也太费空间了.
2.每级的增长也是4K,这样最大的19级也是84K,超过的都放到一起了.是不是考虑增加几级以指数方式增长的
2.内存的分配是按需分配,没有预分配,内存块不是连在一起的,这样是省内存,但内存碎片也会变多.
3.内存的回收比较积极,感觉可能会发现特殊情况时(如一用内存就很多块,用完就一起回收),会频繁发生分配内存.和回收内存(这点不能确认,只是感觉)
4.安全性不够,如果调用了apr_allocator_destroy 释放了内存分配器对象,但有一些内存块已分配出去了,还没回收.这时如果回收,因为对象已经释放,会出现野指针,但函数的内部并没有判断,这是非常危险的,
下面的例子就会出问题,需要在外部进行保护,我感觉做到内部应该更好
apr_allocator_t *myAllocator;
apr_memnode_t *mem1;
apr_memnode_t *mem2;
apr_allocator_create(&myAllocator);
apr_allocator_max_free_set(myAllocator, 4096*50);
mem1 = apr_allocator_alloc(myAllocator,1000);
mem2 = apr_allocator_alloc(myAllocator,2000);
apr_allocator_free(myAllocator,mem1);
apr_allocator_destroy(myAllocator);
apr_allocator_free(myAllocator,mem2);
有什么想法可以发邮件 fastxyf at hotmail.com,一起交流
[结构体]
//内存分配器结构
struct apr_allocator_t {
apr_uint32_t max_index; //空闲队列中,最大的内存块级数
apr_uint32_t max_free_index; //直接回收到系统的内存大小,基数是4096
apr_uint32_t current_free_index;//动态回收到系统的内存大小,是具体的控制,基数是4096,超过后对新回收的内存块,直接回收到系统
apr_thread_mutex_t *mutex; //互拆体
apr_pool_t *owner; //所有都
apr_memnode_t *free[MAX_INDEX];//空闲内存块的链表,分级链表
};
将内存按大小分级存在free列表中,总共分为MAX_INDEX(20)级,
内存的最小块是8192,在free[1]中,以后每级增加4096字节,1-19级每级内的所有块的大小都是一样,
超过19级的都存到free[0]中,free[0]中的大小不是固定的
//单个内存块/内存结点的结构
struct apr_memnode_t {
apr_memnode_t *next; /**< next memnode */
apr_memnode_t **ref; /**< reference to self */
apr_uint32_t index; /**< size */
apr_uint32_t free_index; /**< how much free */
char *first_avail; /**< pointer to first free memory */
char *endp; /**< pointer to end of free memory */
};
[函数]
创建一个内存分配器,创建时并没有分配内存块
apr_status_t apr_allocator_create (apr_allocator_t **allocator)
销毁一个已经存在的内存分配器,销毁时,先释放里面的空闲内存块
void apr_allocator_destroy (apr_allocator_t *allocator)
从内存分配器中分配一块内存块
apr_memnode_t* apr_allocator_alloc (apr_allocator_t *allocator, apr_size_t size)
回收一块内存块或一链表内存块回内存分配器
void apr_allocator_free (apr_allocator_t *allocator, apr_memnode_t *memnode)
设置所有者,只是用来标识的,不设也没事
void apr_allocator_owner_set (apr_allocator_t *allocator, apr_pool_t *pool)
获取所有者
apr_pool_t * apr_allocator_owner_get (apr_allocator_t *allocator)
设置直接回收到系统的内存块大小,超过的内存块在回收时直接释放到系统,而不是存到内存分配器
void apr_allocator_max_free_set (apr_allocator_t *allocator, apr_size_t size)
设置互拆体,多线程时一定要用到
void apr_allocator_mutex_set (apr_allocator_t *allocator, apr_thread_mutex_t *mutex)
获取内存分配器中使用的互拆体
apr_thread_mutex_t * apr_allocator_mutex_get (apr_allocator_t *allocator)
主要是两个结构的理解,和内存的分配的回收
内存分配
1.按存需求的内存块大小,先用4096对齐,再确定对应的级数index.
/* Round up the block size to the next boundary, but always
* allocate at least a certain size (MIN_ALLOC).
*/
size = APR_ALIGN(size + APR_MEMNODE_T_SIZE, BOUNDARY_SIZE);
if (size < MIN_ALLOC)
size = MIN_ALLOC;
/* Find the index for this node size by
* dividing its size by the boundary size
*/
index = (size >> BOUNDARY_INDEX) - 1;
if (index > APR_UINT32_MAX) {
return NULL;
}
2.如果当前的级数(index)没有超过队列中已有的最大级数(max_index) index <= allocator->max_index,按最小适配原则,从低级往高级,找到空闲内存块返回
max_index = allocator->max_index;
ref = &allocator->free[index];
i = index;
while (*ref == NULL && i < max_index) {
ref++;
i++;
}
如果是最大的级数,且是最后一个节点,则调整max_index
if ((*ref = node->next) == NULL && i >= max_index) {
do {
ref--;
max_index--;
}
while (*ref == NULL && max_index > 0);
allocator->max_index = max_index;
}
调大current_free_index 的大小,增大回收的门槛. 但最大不能超过max_free_index
allocator->current_free_index += node->index;
if (allocator->current_free_index > allocator->max_free_index)
allocator->current_free_index = allocator->max_free_index;
3.如果当前的级数(index)超过队列中已有的最大级数(max_index),且free[0]队列不为空,则到free[0]中去查找有无合适的块,找到就返回
while ((node = *ref) != NULL && index > node->index)
ref = &node->next;
也调大current_free_index 的大小,增大回收的门槛. 但最大不能超过max_free_index
4.如果上面的2,3都没有找到,说明没有够到的空闲块,则在物理内存中分配新块.
if ((node = malloc(size)) == NULL)
return NULL;
node->next = NULL;
node->index = (APR_UINT32_TRUNC_CAST)index;
node->first_avail = (char *)node + APR_MEMNODE_T_SIZE;
node->endp = (char *)node + size;
内存回收
这里的内存回收,如果存进来有后续结点,则后续结点也一起回收,即回收的是内存队列
1.设置了回收的内存大小,且当前回收的内存块的级数index大于当前的回收级数current_free_index,则直接放到释放队列freelist中
if (max_free_index != APR_ALLOCATOR_MAX_FREE_UNLIMITED
&& index > current_free_index) {
node->next = freelist;
freelist = node;
}
2.不符合回收到系统的,就回收到相应的队列中
if (index < MAX_INDEX) {
/* Add the node to the appropiate 'size' bucket. Adjust
* the max_index when appropiate.
*/
if ((node->next = allocator->free[index]) == NULL
&& index > max_index) {
max_index = index;
}
allocator->free[index] = node;
current_free_index -= index;
}
else {
/* This node is too large to keep in a specific size bucket,
* just add it to the sink (at index 0).
*/
node->next = allocator->free[0];
allocator->free[0] = node;
current_free_index -= index;
}
回收后,调整current_free_index 的大小,降低回收的门槛.
3.如果有后续结点,则接着处理,直到结束
4.全部处理完后,释放freelist中的节点给系统
这里面最难理解的是current_free_index ,是一个动态调整的值,跟据分配内存的情况,分配时,增加current_free_index的大小,但最大不会超过设置的max_free_index值
回收时,只要有回收到队列中,就降低current_free_index的大小,这样就保证队列中大节点的内存块不会太多.也不会出现有某级结点空的情况
我感觉存在几个问题:
1.每块最小8K好像太大了,如果我是处理音频,压缩后一般就60-150字节,或是网络包,UDP最大才1.4K,那也太费空间了.
2.每级的增长也是4K,这样最大的19级也是84K,超过的都放到一起了.是不是考虑增加几级以指数方式增长的
2.内存的分配是按需分配,没有预分配,内存块不是连在一起的,这样是省内存,但内存碎片也会变多.
3.内存的回收比较积极,感觉可能会发现特殊情况时(如一用内存就很多块,用完就一起回收),会频繁发生分配内存.和回收内存(这点不能确认,只是感觉)
4.安全性不够,如果调用了apr_allocator_destroy 释放了内存分配器对象,但有一些内存块已分配出去了,还没回收.这时如果回收,因为对象已经释放,会出现野指针,但函数的内部并没有判断,这是非常危险的,
下面的例子就会出问题,需要在外部进行保护,我感觉做到内部应该更好
apr_allocator_t *myAllocator;
apr_memnode_t *mem1;
apr_memnode_t *mem2;
apr_allocator_create(&myAllocator);
apr_allocator_max_free_set(myAllocator, 4096*50);
mem1 = apr_allocator_alloc(myAllocator,1000);
mem2 = apr_allocator_alloc(myAllocator,2000);
apr_allocator_free(myAllocator,mem1);
apr_allocator_destroy(myAllocator);
apr_allocator_free(myAllocator,mem2);
有什么想法可以发邮件 fastxyf at hotmail.com,一起交流