Chinaunix首页 | 论坛 | 博客
  • 博客访问: 268721
  • 博文数量: 84
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 927
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-06 23:00
个人简介

growing

文章分类

全部博文(84)

文章存档

2017年(6)

2016年(61)

2015年(17)

我的朋友

分类: C/C++

2016-05-29 13:08:39

说明:空间配置器一直在各个容器背后默默付出,为他们分配内存,并可以有效地解决内存碎片的问题。

SGI STL的默认配置器为alloc,SGI设计了双层配置器。
第一层为所分配内存大于128时使用的,直接调用malloc和free来分配与释放,并且可以设定一个set_new_handler来处理内存不足的情况。
第二层则是小于128时使用的,为防止内存碎片利用一个free lists来控制内存,内存不足时转回第一层方法(因为里面有set_new_handler)。

下面来分析stl中alloc的源码。
1.第一层
  1. template <int inst>
  2. class __malloc_alloc_template {

  3. private:

  4. static void *oom_malloc(size_t);//oom即out of memory,当内存不足时用它来处理

  5. static void *oom_realloc(void *, size_t);

  6. public:

  7. static void * allocate(size_t n)
  8. {
  9.     void *result = malloc(n);//可以看到第一层直接调用了malloc
  10.     if (0 == result) result = oom_malloc(n);//内存不足时用了oom来处理
  11.     return result;
  12. }

  13. static void deallocate(void *p, size_t /* n */)
  14. {
  15.     free(p);//第一层释放直接是free
  16. }

  17. static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
  18. {
  19.     void * result = realloc(p, new_sz);
  20.     if (0 == result) result = oom_realloc(p, new_sz);
  21.     return result;
  22. }

  23. //指定自己的out_of_memory_handler,看到前面已经定义了void (* __malloc_alloc_oom_handler)() = 0,我们可以改变它写成自己的处理方法。
  24. static void (* set_malloc_handler(void (*f)()))()
  25. {
  26.     void (* old)() = __malloc_alloc_oom_handler;
  27.     __malloc_alloc_oom_handler = f;
  28.     return(old);
  29. }

  30. };
然后来分析一下当我们内存不足时alloc是怎么做的:
  1. template <int inst>
  2. void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
  3. {
  4.     void (* my_malloc_handler)();
  5.     void *result;

  6.     for (;;) {
  7.         my_malloc_handler = __malloc_alloc_oom_handler;//这里写了一个死循环来不断的获取内存,直到得到之后就返回
  8.         if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }//当我们没有定义自己的handler时直接抛出异常
  9.         (*my_malloc_handler)();//调用之前定义的处理机制,企图释放内存
  10.         result = malloc(n);//再次获取一下
  11.         if (result) return(result);
  12.     }
  13. }

  14. template <int inst>
  15. void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)//realloc和malloc是一样的道理
  16. {
  17.     void (* my_malloc_handler)();
  18.     void *result;

  19.     for (;;) {
  20.         my_malloc_handler = __malloc_alloc_oom_handler;
  21.         if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
  22.         (*my_malloc_handler)();
  23.         result = realloc(p, n);
  24.         if (result) return(result);
  25.     }
  26. }
2.第二层。
第二层的做法是,维护一个free-list,每次分配一个大块内存(内存池),当有内存需求时,直接从free-list中取,整体是这样的:

当内存池里内存不够时是,在调用refill填充内存,具体代码分析如下:

  1. template <bool threads, int inst>
  2. class __default_alloc_template {

  3. private:

  4.     enum {__ALIGN = 8};//第二层分配的里最小内存块
  5.     enum {__MAX_BYTES = 128};//第二层里分配的最大内存块
  6.     enum {__NFREELISTS = __MAX_BYTES/__ALIGN};//free-list的个数

  7.   static size_t ROUND_UP(size_t bytes) {
  8.         return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));//算所需内存应该分到多大的空间
  9.   }
  10. __PRIVATE:
  11.   union obj {
  12.      //free-list的结点构造,这里用一个联合体,一个指向和自己相同的结点,一个指向实际区块,防止为了维护链表指针而造成的空间浪费
  13.         union obj * free_list_link;
  14.         char client_data[1]; /* The client sees this. */
  15.   };
  16. private:
  17.   static obj * __VOLATILE free_list[__NFREELISTS]; //16个free-list
  18.   static size_t FREELIST_INDEX(size_t bytes) {
  19.         return (((bytes) + __ALIGN-1)/__ALIGN - 1);//算对应free-list的下标
  20.   }
  21.   static void *refill(size_t n);//填充内存
  22.   static char *chunk_alloc(size_t size, int &nobjs);//从内存里取nobjs个大小为size区块,若内存不够可以少于nobjs

  23.   // Chunk allocation state.
  24.   static char *start_free;//内存池起始位置
  25.   static char *end_free;//内存池结束为止
  26.   static size_t heap_size;
  27. public:

  28.   /* n must be > 0 */
  29.   static void * allocate(size_t n)
  30.   {
  31.     obj * __VOLATILE * my_free_list;
  32.     obj * __RESTRICT result;

  33.     if (n > (size_t) __MAX_BYTES) {
  34.         return(malloc_alloc::allocate(n));//大于128就调用第一层配置器
  35.     }

  36.     my_free_list = free_list + FREELIST_INDEX(n);//寻找16个free-list中合适的一个
  37.     result = *my_free_list;
  38.     if (result == 0) {
  39.         void *r = refill(ROUND_UP(n));//没找到可用的就去填充
  40.         return r;
  41.     }
  42.     *my_free_list = result -> free_list_link;
  43.     return (result);
  44.   };

  45.   /* p may not be 0 */
  46.   static void deallocate(void *p, size_t n)
  47.   {
  48.     obj *q = (obj *)p;
  49.     obj * __VOLATILE * my_free_list;

  50.     if (n > (size_t) __MAX_BYTES) {
  51.         malloc_alloc::deallocate(p, n);//大于128就调用第一层配置器
  52.         return;
  53.     }
  54.     my_free_list = free_list + FREELIST_INDEX(n);//寻找16个free-list中对应的一个

  55.     q -> free_list_link = *my_free_list;//回收区块
  56.     *my_free_list = q;
  57.     // lock is released here
  58.   }

  59.   static void * reallocate(void *p, size_t old_sz, size_t new_sz);

  60. } ;
  具体函数实现:

  1. template <bool threads, int inst>
  2. char*
  3. __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
  4. {
  5.     char * result;
  6.     size_t total_bytes = size * nobjs;
  7.     size_t bytes_left = end_free - start_free;//内存池剩余空间

  8.     if (bytes_left >= total_bytes) {//内存池剩完全的够用
  9.         result = start_free;
  10.         start_free += total_bytes;
  11.         return(result);
  12.     } else if (bytes_left >= size) {//内存池剩的不够nobjs个,但是还有几个
  13.         nobjs = bytes_left/size;
  14.         total_bytes = size * nobjs;
  15.         result = start_free;
  16.         start_free += total_bytes;
  17.         return(result);
  18.     } else {
  19.         //内存池连一个都无法提供了
  20.         size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
  21.         // Try to make use of the left-over piece.让剩余的残余内存有些价值
  22.         if (bytes_left > 0) {
  23.             //把剩余的零头凑起来分配给合适的free-list

  24.             //首先找适合的ree-list
  25.             obj * __VOLATILE * my_free_list =
  26.                         free_list + FREELIST_INDEX(bytes_left);
  27.             //调整,将零头放进去
  28.             ((obj *)start_free) -> free_list_link = *my_free_list;
  29.             *my_free_list = (obj *)start_free;
  30.         }

  31.         //配置heap,来补充内存池
  32.         start_free = (char *)malloc(bytes_to_get);
  33.         if (0 == start_free) {
  34.             int i;
  35.             obj * __VOLATILE * my_free_list, *p;
  36.             // Try to make do with what we have. That can't
  37.             // hurt. We do not try smaller requests, since that tends
  38.             // to result in disaster on multi-process(多进程) machines.
  39.             for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
  40.                 my_free_list = free_list + FREELIST_INDEX(i);
  41.                 p = *my_free_list;
  42.                 if (0 != p) {
  43.                     //若free-list还有没有用的区块,调整并释放
  44.                     *my_free_list = p -> free_list_link;
  45.                     start_free = (char *)p;
  46.                     end_free = start_free + i;

  47.                     //递归调整自己来修正nobjs
  48.                     return(chunk_alloc(size, nobjs));
  49.                     // Any leftover piece will eventually make it to the
  50.                     // right free list.
  51.                 }
  52.             }
  53.      end_free = 0;    // In case of exception.啥都没了,又用第一层,靠他的out_of_memory_handler了
  54.             start_free = (char *)malloc_alloc::allocate(bytes_to_get);
  55.             // This should either throw an
  56.             // exception or remedy the situation. Thus we assume it
  57.             // succeeded.
  58.         }
  59.         heap_size += bytes_to_get;
  60.         end_free = start_free + bytes_to_get;
  61.         return(chunk_alloc(size, nobjs));//递归调整自己来修正nobjs
  62.     }
  63. }


  64. /* Returns an object of size n, and optionally adds to size n free list.*/
  65. /* We assume that n is properly aligned. */
  66. /* We hold the allocation lock. */
  67. template <bool threads, int inst>
  68. void* __default_alloc_template<threads, inst>::refill(size_t n)
  69. {
  70.     int nobjs = 20;
  71.     //尝试获得20个内存区块
  72.     char * chunk = chunk_alloc(n, nobjs);
  73.     obj * __VOLATILE * my_free_list;
  74.     obj * result;
  75.     obj * current_obj, * next_obj;
  76.     int i;

  77.     //只获得了一个,那就用那个吧,没有新的再进去
  78.     if (1 == nobjs) return(chunk);
  79.     my_free_list = free_list + FREELIST_INDEX(n);

  80.     /* Build free list in chunk *///在chunk空间里建立free-list
  81.       result = (obj *)chunk;//这块给客户端用

  82.       *my_free_list = next_obj = (obj *)(chunk + n);
  83.      //将free-list各节点穿起来
  84.       for (i = 1; ; i++) {
  85.          //i从0开始,因为第一个已经给客户端了
  86.         current_obj = next_obj;
  87.         next_obj = (obj *)((char *)next_obj + n);
  88.         if (nobjs - 1 == i) {
  89.             current_obj -> free_list_link = 0;
  90.             break;
  91.         } else {
  92.             current_obj -> free_list_link = next_obj;
  93.         }
  94.       }
  95.     return(result);
  96. }

  97. template <bool threads, int inst>
  98. void*
  99. __default_alloc_template<threads, inst>::reallocate(void *p,
  100.                                                     size_t old_sz,
  101.                                                     size_t new_sz)
  102. {
  103.     void * result;
  104.     size_t copy_sz;

  105.     if (old_sz > (size_t) __MAX_BYTES && new_sz > (size_t) __MAX_BYTES) {
  106.         return(realloc(p, new_sz));
  107.     }
  108.     if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);
  109.     result = allocate(new_sz);
  110.     copy_sz = new_sz > old_sz? old_sz : new_sz;
  111.     memcpy(result, p, copy_sz);
  112.     deallocate(p, old_sz);
  113.     return(result);
  114. }
 以上把关于加锁的代码都剔除了。只看实现方式。
阅读(2209) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~