STL源码剖析之空间配置器

本文深入探讨了STL中的空间配置器概念,包括一级配置器(SGISTL)和二级配置器的工作原理及实现细节。一级配置器通过封装malloc/free并引入异常处理机制来管理内存,而二级配置器则通过内存池和自由链表来减少内存碎片,提高内存管理效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

源代码附在最后,并且会进行详细的注释(个人理解还有STL(侯捷)源码剖析的相关内容)

 

空间配置器:

1.空间配置器是对内存进行管理,内存的申请和释放

2.空间配置器分为两部分

<1>简单空间配置器,也叫一级配置器

<2>基于内存池的空间配置器,也叫二级空间配置器

  

.SGI STL 第一级配置器

malloc / free的简单封装,但是这个提供了异常处理机制。

在以及空间配置器中,有两点需要注意:

<1>.allocate()直接使用malloc()

<2>.模拟C++set_new_handler()以处理内存不足的状况

对于第一级空间配置器,我会在代码的地方进行详细的注释。

第一级空间配置器的allocate()realloc()都是在调用malloc()realloc()失败之后,然后调用oom_malloc()oom_realloc()。后面两个都是内循环,不断的调用内存不足处理例程,期望在某次调用之后,得到足够的内存而完成任务。

 

注:C++set_new_handler()机制是在要求系统在内存分配需求无法满足时,调用一个你所指定的函数,也就是说一旦::operator new 无法完成任务时,在丢出std::bab_alloc异常状态之前,会先调用由客户端指定的处理例程来解决内存不足。

 

二.SGI STL第二级空间配置器

第二级配置器的作用:因为大量的小区块会造成内存碎片,在配置时有额外的负担,应为系统要靠这多出来的空间管理内存。

这个有三点需要注意:

1.一共十六个自由链表(free lists)管理十六个小型区块的配置能力。内存池(memory  pool) 以malloc()配置而得,如果内存不足,转调第一级空间配置器。

2.如果需求区块大于128byes,就转调第一级空间配置器,当区块小于128 byes时,则以内存池管理。

3.配置器除了负责配置还要负责回收。

 

在每一次配置一大块内存时,并维护自由链表(free-list),如果下次有相同大小的内存需求,就之间从free-list中拔取。在二级配置器中,把小额区块内存调为8的倍数,维护16个自由链表,大小分别为:81624324048566472808896104112120128 beys

 

这几张图很重要,可以解开很多疑惑

 

 

 

 

 

  附源码:

  1

  2 #if 1

  3 #include<iostream>

  4 #include<new>

  5 #include<malloc.h>

  6 using namespace std;

  7 //#define __THROW_BAD_ALLOC   throw   bad_alloc

  8 #define __THROW_BAD_ALLOC  cerr<<"Throw bad alloc, Out Of Memory."<<endl;   exit(1)

  9 #elif  !defined  (__THROW_BAD_ALLOC)

 10 #include<iostream.h>

 11 #define __THROW_BAD_ALLOC   cerr<<"out of memory"<<endl; exit(1);

 12 #endif

 13

 14 template<int inst>    //非类型模板

 15 class __malloc_alloc_template

 16 {

 17 private:   // 下面三个函数处理内存不足的情况

 18     static void* oom_malloc(size_t);

 19     static void* oom_realloc(void *, size_t);

 20     static void(* __malloc_alloc_oom_handler)();

 21

 22 public:

23     static void* allocate(size_t n)

 24     {

 25         void *result = malloc(n);  //第一级配置器直间使用malloc()函数

 26         if(0 == result)   // result==NULL如果result为空的时候,改用oom_malloc()

 27             result = oom_malloc(n);

 28         return result; //返回指针

 29     }

 30     static void   deallocate(void *p, size_t) //重新分配

 31     {

 32         free(p); //第一级空间适配器可以直接使用free()函数

 33     }

 34     static void* reallocate(void *p, size_t, size_t new_sz)  

 35     {

 36         void *result = realloc(p, new_sz);    //第一级配置器直间使用malloc()函数

 37         if(0 == result)

 38             oom_realloc(p,new_sz);    如果result为空的时候,改用oom_realloc()

 39         return result;

 40     }

 41 public:

 42  //set_new_handler(Out_Of_Memory); //set_new_handler()以处理内存不足的状况

//仿真C++set_new_handler()

 43     static void(*set_malloc_handler(void(*f)()))()  //这个是函数,不是函数指针

 44     {

 45         void(*old)() = __malloc_alloc_oom_handler;

 46         __malloc_alloc_oom_handler = f;

 47         return old;

 48     }

 49 };

 50

 51 template<int inst>

 52 void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;

 53

 54 template<int inst>

 55 void* __malloc_alloc_template<inst>::oom_malloc(size_t n)

 56 {

 57     void *result;

 58     void(* my_malloc_handler)();

 59

 60     for(;;) //不断的尝试释放、配置、再释放、再配置

 61     {

 62         my_malloc_handler = __malloc_alloc_oom_handler;

 63         if(0 == my_malloc_handler)

 64         {

 65             __THROW_BAD_ALLOC;

 66         }

 67         (*my_malloc_handler)(); //调用处理例程,企图释放内存

 68         result = malloc(n); //再次尝试配置内存

 69         if(result)

 70             return result;

 71     }

 72 }

 73

 74

 75 template<int inst>

 76 void* __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)

 77 {

 78     void(*my_malloc_handler)();

 79     void *result;

 80     for(;;)

 81     {

 82         my_malloc_handler = __malloc_alloc_oom_handler;

 83         if(0 == my_malloc_handler)

 84         {

 85             __THROW_BAD_ALLOC;

 86         }

 87         (*my_malloc_handler)();  //调用处理例程,企图释放内存

 88         result = realloc(p, n);     //再次尝试配置内存

 89         if(result)

 90             return result;

 91     }

 92 }

 93

 94 typedef __malloc_alloc_template<0> malloc_alloc;

 95 //第一级空间配置器结束

 

 96 /////////////////////////////////////////////////////////////////////////////////////

 

 97 //第二级空间配置器

 

 98 enum {__ALIGN = 8}; //小型区块的上调边界

 99 enum {__MAX_BYTES  = 128}; //小型区块的上限

100 enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; //自由链表的个数

101

102 template<bool threads, int inst>

103 class __default_alloc_template

104 {

105 public:

106     static void* allocate(size_t n);

107     static void  deallocate(void *p, size_t n);

108     static void* reallocate(void *p, size_t, size_t new_sz);

109 private:

110     static size_t  ROUND_UP(size_t bytes)  //byes上调到8的倍数

111     {

112         return (((bytes) + __ALIGN-1) & ~(__ALIGN-1));

113     }

114 private:

115     union obj

116     {

117         union obj * free_list_link; //构造自由链表的节点

118         char client_data[1];

119     };

120 private:

121     static obj* volatile free_list[__NFREELISTS];

122     static size_t FREELIST_INDEX(size_t bytes)  //根据区块的大小,决定使用第nfree-listn是从0开始

123     {

124         return ((bytes)+__ALIGN-1) / __ALIGN-1;

125     }

126 private:

127     static char *start_free; //内存池起始位置

128     static char *end_free; //内存池结束位置

129     static size_t heap_size;

130     static void *refill(size_t n); //返回大小为n的对象,并可能加入大小为n的其他区块到free-list

131     static char* chunk_alloc(size_t size, int &nobjs); //配置一大块空间,容纳nobjs个大小为“size”的区块

132 };

133

134

135 template<bool threads, int inst>

136 char* __default_alloc_template<threads, inst>::start_free = 0;

137 template<bool threads, int inst>

138 char* __default_alloc_template<threads, inst>::end_free = 0;

139 template<bool threads, int inst>

140 size_t __default_alloc_template<threads, inst>::heap_size = 0;

141 template<bool threads, int inst>

142 typename __default_alloc_template<threads, inst>::obj* volatile

143 __default_alloc_template<threads, inst>::free_list[__NFREELISTS] =

144 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

145

146 template<bool threads, int inst>

147 void* __default_alloc_template<threads, inst>::allocate(size_t n)

148 {

149     obj * volatile *my_free_list; //二级指针

150     obj *result;

151

152     if(n > __MAX_BYTES) //大于128byes就调用第一配置器

153     {

154         return malloc_alloc::allocate(n);

155     }

156

157     my_free_list = free_list + FREELIST_INDEX(n); //寻找16free-list中最合适的一个

158     result = *my_free_list;

159

160     if(result == 0) //如果没有找到就重新填充free-list

161     {

162         void *r = refill(ROUND_UP(n));

163         return r;

164     }

165

166     *my_free_list = result->free_list_link; //调整free-list

167     return result;

168 }

169

// 重新填充free-list

170 template<bool threads, int inst>

171 void* __default_alloc_template<threads, inst>::refill(size_t n)

172 {

173     int nobjs = 20; //经验值

174     char *chunk = chunk_alloc(n, nobjs); //调用chunk_alloc(),尝试取得nobjs个区块作为free-list的新节点

175     obj * volatile *my_free_list;

176

177     obj *result;

178     obj *current_obj, *next_obj;

179     int i;

180

181     if(1 == nobjs) //如果得到了一个区块,这个区块就分配给调用者

182         return chunk;

183

184  my_free_list = free_list + FREELIST_INDEX(n); //准备调整free-list,纳入新节点

185     result = (obj*)chunk; //返回给客户端

186     *my_free_list = next_obj = (obj*)(chunk+n);

187

188     for(i=1; ; ++i) //从第1个开始,第0个将返回给客户端

189     {

190         current_obj = next_obj;

191         next_obj = (obj*)((char*)next_obj+n);

192         if(nobjs - 1 == i)

193         {

194             current_obj->free_list_link = 0;

195             break;

196         }

197         else

198         {

199             current_obj->free_list_link = next_obj;

200         }

201     }

202     return result;

203 }

204

//内存池

205 template<bool threads, int inst>

206 char* __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int &nobjs)

207 {

208     char *result;

209     size_t total_bytes = size * nobjs;

210     size_t bytes_left = end_free - start_free; //内存池剩余空间

211     if(bytes_left >= total_bytes) //内存池剩余空间完全满足需求量

212     {

213         result = start_free;

214         start_free += total_bytes;

215         return result;

216     }

217     else if(bytes_left >= size) //内存池剩余空间不能满足需求量,但可以提供一个以上的区块

218     {

219         nobjs = bytes_left / size;

220         total_bytes = size * nobjs;

221         result = start_free;

222         start_free += total_bytes;

223         return result;

224     }

225     else //内存池剩余空间连一个区块都无法满足

226     {

227         size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);

228         if(bytes_left > 0) //试着让内存池中残余的零头还有利用价值

229         {

230             obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);

231             ((obj*)start_free)->free_list_link = *my_free_list; //调整free-list,将内存池中残余的空间编入

232             *my_free_list = (obj *)start_free;

233         }

234

235         start_free = (char *)malloc(bytes_to_get); //配置heap空间,用来补充内存池

236         if(0 == start_free) //heap空间不足,malloc失败

237         {

238             int i;

239             obj * volatile *my_free_list, *p;

240             for(i=size; i<=__MAX_BYTES; i += __ALIGN)

241             {

242                 my_free_list = free_list + FREELIST_INDEX(i);

243                 p = *my_free_list;

244                 if(0 != p) //free-list内有未用区块,调整free-list释放出未用区块

245                 {

246                     *my_free_list = p->free_list_link;

247                     start_free = (char *)p;

248                     end_free = start_free + i; //递归调用自己,为了修正nobjs

249                     return chunk_alloc(size, nobjs); //将残余的编入free-list中备用

250                 }

251             }

252             end_free = 0; //如果出现错误,调用第一级配置器

253             start_free = (char *)malloc_alloc::allocate(bytes_to_get);

254         }

255

256         heap_size  += bytes_to_get;

257         end_free = start_free + bytes_to_get; //递归调用自己,为了修正nobjs

258         return chunk_alloc(size, nobjs);

259     }

260 }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值