关闭

Apache 中使用的 APR allocator 分析

1785人阅读 评论(0) 收藏 举报
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,一起交流
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:38415次
    • 积分:542
    • 等级:
    • 排名:千里之外
    • 原创:9篇
    • 转载:6篇
    • 译文:0篇
    • 评论:46条
    文章分类
    最新评论