305-STL的空间配置器(1)

梦里梦外; 2023-01-19 09:46 193阅读 0赞

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
头文件专门用于管理内存,内存的申请和释放(我们可以想象成operator new)
头文件,专门管理某个内存空间中的构建或者析构对象。
头文件是对大块内存的填充和拷贝

在这里插入图片描述
在多线程情况下,需要加锁和解锁。
在这里插入图片描述
当这个内存块的大小大于128字节,我们认为这个块足够大,把它交给第一级配置器(直接使用malloc和free)去配置。
当这个内存块的大小小于128字节,我们认为这个块小,为了降低额外负担(我们malloc分配一块内存时,真实的情况是,系统分配的内存大小远大于我们申请的大小,包含了4字节的上越界标记,4字节的下越界标记和28字节的头部信息,造成内存和时间效率降低),,我们把它交给内存池,第二级配置器。
在这里插入图片描述

封装第一级配置器

包装了C语言的malloc ,free , realloc

  1. #ifndef YHP_ALLOC_H
  2. #define YHP_ALLOC_H
  3. #include<iostream>
  4. namespace yhp
  5. {
  6. #if 0
  7. #include<new>
  8. #define __THROW_BAD_ALLOC throw std::bad_alloc;
  9. #elif !defined(__THREAD_BAD_ALLOC)//如果没有定义 ,就打印内存溢出
  10. //#include<iostream>
  11. #define __THROW_BAD_ALLOC std::cerr<<"out of memory"<<std::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 n)//分配n个空间
  19. //要么能申请到空间然后走人,要么抛出异常
  20. {
  21. void * result = NULL;
  22. void (*my_malloc_handler)() = NULL;
  23. for(;;)//无限循环
  24. {
  25. my_malloc_handler = __malloc_alloc_oom_handler;
  26. if(NULL == my_malloc_handler)
  27. {
  28. __THROW_BAD_ALLOC;//调用宏,抛出异常
  29. }
  30. (*my_malloc_handler)();
  31. //回调函数,1、释放更多的空间2、指明新的释放函数3、如果没有指明新的释放函数把程序结束掉
  32. result = malloc(n);
  33. if(result != NULL)
  34. {
  35. return result;//成功了,内存的返回
  36. }
  37. }
  38. }
  39. static void *oom_realloc(void *p,size_t new_sz)//追加内存的分配
  40. {
  41. void * result = NULL;
  42. void (*my_malloc_handler)() = NULL;
  43. for(;;)
  44. {
  45. my_malloc_handler = __malloc_alloc_oom_handler;
  46. if(NULL == my_malloc_handler)
  47. {
  48. __THROW_BAD_ALLOC;
  49. }
  50. (*my_malloc_handler)();
  51. result = realloc(p,new_sz);
  52. if(result != NULL)
  53. {
  54. return result;
  55. }
  56. }
  57. }
  58. static void (*__malloc_alloc_oom_handler)();//函数指针
  59. public:
  60. static void *allocate(size_t n)//n是字节个数,不是元素的个数,内存的分配
  61. {
  62. void *result = malloc(n);
  63. if(NULL == result)
  64. {
  65. result = oom_malloc(n);
  66. }
  67. return result;
  68. }
  69. static void deallocate(void *p,size_t n)//释放内存空间
  70. {
  71. free(p);
  72. }
  73. static void *reallocate(void *p,size_t old_sz,size_t new_sz)//追加内存空间
  74. {
  75. void *result = realloc(p,new_sz);
  76. if(NULL == result)
  77. {
  78. result = oom_realloc(p,new_sz);
  79. }
  80. return result;
  81. }
  82. static void (*set_malloc_handler(void (*f)()) )()
  83. {
  84. void (*old)() = __malloc_alloc_oom_handler;
  85. __malloc_alloc_oom_handler = f;
  86. return old;
  87. }
  88. };
  89. template<int inst>
  90. void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = NULL;
  91. ///
  92. typedef __malloc_alloc_template<0> malloc_alloc;

测试代码如下
在这里插入图片描述

如果我们在程序的运行过程中,内存不足了怎么办?

进入主函数之前,p1,p2指向相应的申请的内存空间。
在这里插入图片描述
我们写了一个函数yhp,把p1释放掉,把第二个函数rj放进去,
rj这个函数的作用是释放p2,如果释放空间后,把空放进去。

在这里插入图片描述
然后我们书写主函数

在这里插入图片描述
运行程序,我们看到,首先调用set_malloc_handler,把这个函数指针指向hp这个函数,申请空间的时候,调动malloc,如果我把result置为空,要调动oom_malloc,my_malloc_handler获得了hp的指针,不为空,调动hp的函数释放更多的空间,把rj这个函数注册进去,假设我再把result置为空,没有申请到空间,my_malloc_handler获得了rj的指针,不为空,再调动rj的释放内存函数,malloc再申请,我们又给个result=0,my_malloc_handler变成了空,调动宏,打印了错误,程序退出,内存用完了。
也就是说,先开始估算这个程序所需要的内存,直接先占领,我的程序在运行的过程中,如果内存不足,通过oom_malloc,调动回调函数,首先指向hp函数,释放内存,再申请空间,释放内存,如果还要申请,为空了,就确实是内存用完了。
注意:rj的函数里直接置为空放进去。防止如果再调用回调函数,那个回调函数中又占用了空间,但是内存不足,从而造成了死循环。

第二级配置器

在这里插入图片描述
在这里插入图片描述
如下图,最上面的一排数字代表内存的大小。下排的数字代表数组的下标
在这里插入图片描述
在最开始的时候,都是空。假设我们需要12个字节,选取 1下标对应的16个字节的内存块,没有找到,我们就先malloc 16*20 *2=640个字节的内存空间,进行注水,提取出320字节,把16个字节给给用户,剩下19个16字节块变成一个块一个块,一个块大小为16个字节,以链表的形式,链起来,头结点指向1下标的位置,如果我们还需要16个字节,就使用链表的头删,
在这里插入图片描述
这样比malloc速度快多了,而且这16个字节是没有上越界下越界标记和头部信息的。
如果我们需要40个字节,下标4的位置为空,STL就要把剩下的320字节分了,40乘以20大于320,320除以40是8,刚好分配8个块出去。把第一个块给用户,剩下的字节划分为7个块,划分成一块一块大小为40字节,以链表的形式链到4下标的位置。如果我们又需要70字节,找到8下标,为空,池子里面没有水了,就再malloc 72x20x2个字节的内存块,注水,把池子分为一部分操作。

函数实现的解释

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
obj是一个联合体,类型成员
在这里插入图片描述
在这里插入图片描述
free_list
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第二适配器的代码实现

  1. //第二级适配器
  2. enum {
  3. __ALIGN = 8};//起始块的大小,8的倍数
  4. enum {
  5. __MAX_BYTES = 128};//最大字节个数是128
  6. enum {
  7. __NFREELISTS = __MAX_BYTES / __ALIGN }; //自由链的大小是16
  8. template<bool threads,int ints>//是否是多线程
  9. class __default_alloc_template//默认空间配置器
  10. {
  11. private:
  12. union obj//这个结构就是一个一个的块
  13. {
  14. union obj * free_list_link;// 相当于next,链
  15. char client_data[1];//客户
  16. };
  17. private:
  18. static obj * volatile free_list[__NFREELISTS];//从内存查,数组下标0 1 ... 15
  19. static char *start_free;//指向池子的开头
  20. static char *end_free;//指向池子的结尾
  21. static size_t heap_size;//累计从堆区申请的空间大小
  22. static size_t ROUND_UP(size_t bytes)//变成8的倍数
  23. {
  24. return (bytes + __ALIGN - 1) & ~(__ALIGN - 1);
  25. }
  26. static size_t FREELIST_INDEX(size_t bytes)//计算申请空间大小的对应块的大小,返回下标
  27. {
  28. return (bytes + __ALIGN -1) / __ALIGN - 1;
  29. }
  30. static char *chunk_alloc(size_t size,int &nobjs)//往池子里注水
  31. {
  32. // 16 20
  33. char *result = NULL;
  34. size_t total_bytes = size * nobjs; //总的大小
  35. size_t bytes_left = end_free - start_free;//大小
  36. if(bytes_left >= total_bytes) // 满足了20块
  37. {
  38. result = start_free;
  39. start_free = start_free + total_bytes;
  40. return result;
  41. }else if(bytes_left >= size) //不够20个块,看看能不能足够1块
  42. {
  43. nobjs = bytes_left / size;//新的块
  44. total_bytes = size*nobjs; //新的大小
  45. result = start_free;
  46. start_free = start_free + total_bytes;
  47. return result;
  48. }else//连一块都不够
  49. {
  50. size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size>>4);//申请新的池子
  51. if(bytes_left > 0)//剩下的但是不够的内存填充进去
  52. {
  53. obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);
  54. ((obj*)start_free)->free_list_link = *my_free_list;
  55. *my_free_list = (obj*)start_free;
  56. }
  57. start_free = (char*)malloc(bytes_to_get);
  58. if(NULL == start_free)//通过malloc申请空间失败了
  59. {
  60. obj * volatile * my_free_list = NULL;
  61. for(int i = size ; i <= __MAX_BYTES; i += __ALIGN )//每次加8,扫描
  62. {
  63. my_free_list = free_list + FREELIST_INDEX(i);
  64. obj * p = *my_free_list;
  65. if(p != NULL)//不为空
  66. {
  67. *my_free_list = p->free_list_link;
  68. start_free = (char*)p;
  69. end_free = start_free + i;
  70. return chunk_alloc(size,nobjs);//是原来池子上的内存
  71. }
  72. }
  73. start_free = (char*)malloc_alloc::allocate(bytes_to_get);
  74. }
  75. end_free = start_free + bytes_to_get;
  76. heap_size += bytes_to_get;
  77. return chunk_alloc(size,nobjs);
  78. }
  79. }
  80. static void *refill(size_t n) //把池子里的空间拿出来进行分块,链起来, n byte num ; n % 8 == 0;
  81. {
  82. int nobjs = 20;
  83. char *chunk = chunk_alloc(n,nobjs);//注水20个块
  84. if(1 == nobjs)
  85. {
  86. return chunk;//?
  87. }
  88. obj * volatile * my_free_list = NULL;
  89. obj * result = (obj*)chunk;
  90. obj * current_obj = NULL, *next_obj = NULL;
  91. int i = 0;
  92. my_free_list = free_list + FREELIST_INDEX(n);
  93. *my_free_list = next_obj = (obj*)(chunk + n);
  94. for(i = 1; ; ++i)
  95. {
  96. current_obj = next_obj;
  97. next_obj = (obj*)((char*)next_obj + n);
  98. if(i == nobjs - 1)
  99. {
  100. current_obj->free_list_link = NULL;
  101. break;
  102. }
  103. current_obj->free_list_link = next_obj;
  104. }
  105. return result;
  106. }
  107. public:
  108. static void *allocate(size_t n)// 申请空间,n byte num;
  109. {
  110. if(n > (size_t)__MAX_BYTES)//大于128
  111. {
  112. return malloc_alloc::allocate(n);//调用第一级适配器
  113. }
  114. obj * volatile * my_free_list = NULL;//二级指针
  115. obj * result = NULL;//一级指针
  116. my_free_list = free_list + FREELIST_INDEX(n);
  117. result = *my_free_list;
  118. if(NULL == result)
  119. {
  120. void *r = refill(ROUND_UP(n));// n 12 = 16
  121. return r;
  122. }
  123. *my_free_list = result->free_list_link;
  124. return result;
  125. }
  126. static void deallocate(void *p,size_t n)//释放空间
  127. {
  128. if(n > (size_t) __MAX_BYTES)
  129. {
  130. malloc_alloc::deallocate(p,n);
  131. return ;
  132. }
  133. obj *q = (obj*)p;//把块还回去
  134. obj * volatile * my_free_list = free_list + FREELIST_INDEX(n);//12//16
  135. q->free_list_link = *my_free_list;
  136. *my_free_list = q;//把块链接起来
  137. return ;
  138. } // malloc realloc ,
  139. static void *reallocate(void *p,size_t old_sz,size_t new_sz)//追加空间
  140. {
  141. if(old_sz > __MAX_BYTES && new_sz > __MAX_BYTES)
  142. {
  143. return malloc_alloc::reallocate(p,old_sz,new_sz);
  144. }
  145. if(ROUND_UP(old_sz) == ROUND_UP(new_sz))// 12 14 // 16
  146. {
  147. return p;//足够了,不需要重新分配
  148. }
  149. // old_sz < 128 new_sz < 128;
  150. // > <
  151. // < >
  152. size_t sz = old_sz < new_sz ? old_sz:new_sz;
  153. void *s = allocate(new_sz);//调动一级适配器
  154. memmove(s,p,sz);
  155. deallocate(p,old_sz);//释放p
  156. return s;
  157. }
  158. };
  159. template<bool threads,int ints>
  160. typename __default_alloc_template<threads,ints>::obj * volatile
  161. __default_alloc_template<threads,ints>::free_list[__NFREELISTS]={
  162. NULL};
  163. template<bool threads,int ints>
  164. char * __default_alloc_template<threads,ints>::start_free = NULL;
  165. template<bool threads,int ints>
  166. char * __default_alloc_template<threads,ints>::end_free = NULL;
  167. template<bool threads,int ints>
  168. size_t __default_alloc_template<threads,ints>::heap_size = 0;

书写

  1. #ifndef YHP_CONSTRUCT_H
  2. #define YHP_CONSTRUCT_H
  3. #include"yhp_iterator.h"
  4. #include"yhp_type_traits.h"
  5. namespace yhp
  6. {
  7. template<class T1,class T2>
  8. inline void construct(T1 *p,const T2 &val)//带参数的构造函数
  9. {
  10. new(p) T1(val);//构建对象并初始化,定位new
  11. }
  12. template<class T>
  13. inline void construct(T *p)//不带参数的构造函数
  14. {
  15. new(p) T();
  16. }
  17. template<class T>
  18. inline void destroy(T *p)//销毁函数
  19. {
  20. p->~T();
  21. }
  22. template<class _FI>
  23. inline void destroy(_FI _F, _FI _L)
  24. {
  25. __destroy(_F,_L,value_type(_F));
  26. }
  27. template<class _FI,class T>
  28. inline void __destroy(_FI _F,_FI _L , T *)
  29. {
  30. __type_traits<T>();
  31. typedef typename __type_traits<T>::has_trivial_destructor dest;
  32. __destroy_aux(_F,_L,dest());
  33. }
  34. template<class _FI>
  35. inline void __destroy_aux(_FI _F,_FI _L , __true_type)
  36. {
  37. }
  38. template<class _FI>
  39. inline void __destroy_aux(_FI _F,_FI _L , __false_type)
  40. {
  41. for(; _F != _L; ++_F)
  42. {
  43. destroy(&*_F);
  44. }
  45. }
  46. }
  47. #endif

发表评论

表情:
评论列表 (有 0 条评论,193人围观)

还没有评论,来说两句吧...

相关阅读

    相关 STL空间配置

    最近在看侯捷老师写的STL源码剖析,想通过看优秀的代码来提高自己的编程水平。 首先STL提供了六大组件,彼此可以套用使用。 借助一张从书上截的图来表明: ![这里写