图解STL内存管理的两种边界情况(STL源码剖析补充)
第一种情况就是内存池剩余的小字节空间怎么处理,会不会有内存泄露,答案肯定是不会,但是这个过程是怎么处理的,以下的代码已经简化处理,直接放到VS2010里就可以运行
#include<stdio.h> #include<stdlib.h> static const size_t __ALIGN=8; static const size_t __MAX_BYTES=128; static const size_t __NFREELISTS=__MAX_BYTES/__ALIGN; class __default_alloc_template { private: //讲bytes上调至8的倍数 static size_t ROUND_UP(size_t bytes) { //高效率版本,没有用到乘法器,当比8多1时,就应该加倍9+7=16,多2时9+8=17,然后与1000000变成16,为8的整倍数 return (((bytes)+__ALIGN-1)&~(__ALIGN-1)); } //free_list节点构造 public: union obj { union obj* free_list_link; char client_data[1]; }; private: static obj *volatile free_list[__NFREELISTS]; //根据块大小,选择使用第n号的free_list,n从0开始 static size_t FREELIST_INDEX(size_t bytes) { return (((bytes)+__ALIGN-1)/__ALIGN-1); } public: static void *refill(size_t n); static char *chunk_alloc(size_t size,int &nobjs); public: //内存分配状态 static char *start_free; static char *end_free; static size_t heap_size; static size_t nmalloc; public: static void *allocate(size_t n) { obj *volatile *my_free_list; obj *result; //大于128调用第一级配置器,这里忽略大内存的处理 //在16个链表里面找到合适的free_list my_free_list=free_list+FREELIST_INDEX(n); result=*my_free_list; if(result==0) { void *r=refill(ROUND_UP(n)); return r; } //调整free_list,返回分配的空间,自由链表指向余下的空间首地址..会用到字节对齐? *my_free_list=result->free_list_link; printf("把%d字节的空间给容器使用\n",n); return result; } static void *deallocate(void *p,size_t n) { } static void *reallocate(void *p,size_t old_sz,size_t new_sz) { } }; char *__default_alloc_template::start_free=0; char *__default_alloc_template::end_free=0; size_t __default_alloc_template::heap_size=0; size_t __default_alloc_template::nmalloc=2; __default_alloc_template::obj *volatile __default_alloc_template::free_list[__NFREELISTS]= {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; void *__default_alloc_template::refill(size_t n) { int nobjs=20; char *chunk=chunk_alloc(n,nobjs); obj *volatile *my_free_list; obj *result; obj *current_obj,*next_obj; int i; if(1==nobjs) //chunk_alloc函数里修改了nobjs return (chunk); my_free_list=free_list+FREELIST_INDEX(n); result=(obj*)chunk; *my_free_list=next_obj=(obj*)(chunk+n); //以下将free list的各个节点串联起来 for(i=1;;i++) { current_obj=next_obj; next_obj=(obj*)((char*)next_obj+n); if(nobjs-1==i) { current_obj->free_list_link=0; break; } else current_obj->free_list_link=next_obj; } } char *__default_alloc_template::chunk_alloc(size_t size,int &nobjs) { char *result; size_t total_bytes=size*nobjs; size_t bytes_left=end_free-start_free;//内存池剩余空间 if(bytes_left>=total_bytes) { result=start_free; start_free+=total_bytes; printf("内存池拨出%d个%d字节空间,总共%d字节给%d字节自由链表,内存池剩下%d字节空间\n",nobjs,size,nobjs*size,size,end_free-start_free); return (result); } else if (bytes_left>=size) { nobjs=bytes_left/size; total_bytes=size*nobjs; result=start_free; start_free+=total_bytes; printf("内存池拨出%d个%d字节空间,总共%d字节给%d字节自由链表,内存池剩下%d字节空间\n",nobjs,size,nobjs*size,size,end_free-start_free); return result; } else { //一个块大小的内存都没有了 //把内存池接到另外一块的头,不分配不使用任何空间 size_t bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4);//? if(bytes_left>0) { //内存池还有一些零头,把他接到另外一个链表的头那里 obj *volatile *my_free_list=free_list+FREELIST_INDEX(bytes_left); printf("内存池拨出%d字节给自由链表%d\n",bytes_left,FREELIST_INDEX(bytes_left)); ((obj*)start_free)->free_list_link=*my_free_list; *my_free_list=(obj*)start_free; } if(nmalloc>0) { start_free=(char*)malloc(bytes_to_get); printf("申请了%d字节的空间\n",bytes_to_get); nmalloc--; } else start_free=0; if(0==start_free) { //heap空间不足 int i; obj *volatile *my_free_list,*p; for(i=size;i<=__MAX_BYTES;i+=__ALIGN) { my_free_list=free_list+FREELIST_INDEX(i); p=*my_free_list; if(0!=p) { *my_free_list=p->free_list_link; start_free=(char*)p; end_free=start_free+i; //递归调用自己,修正nbojs return chunk_alloc(size,nobjs); } } end_free=0; printf("out of memory\n"); } heap_size+=bytes_to_get; end_free=start_free+bytes_to_get; //递归调用自己,为了修正nobjs return chunk_alloc(size,nobjs); } } typedef __default_alloc_template alloc; //allocator包装类 template<class T,class Alloc> class simple_alloc { public: static T *allocate(size_t n) { return 0==n?0:(T*)Alloc::allocate(n*sizeof(T)); } //以下先省略... }; void main() { simple_alloc<int,alloc> data_allocator; data_allocator.allocate(1); data_allocator.allocate(16); data_allocator.allocate(32); }
结果
过程分析
1.分配20个8字节的空间给自由链表,剩下160个字节留给内存池
2.要分配64字节的块,160字节只够分配2个空间,将剩下32个字节
3.要分配128字节的块,还有32字节怎么办,先把32字节分配给自由链表,但未使用
4.再分配20*128字节的空间给自由链表#15,剩下2560+heap_size>>4空间给内存池
第二部分是内存耗尽的特殊情况,为了模拟内存耗尽,我们需要稍微修改一下代码,设置成申请一次之后就不能再申请内存了
#include<stdio.h> #include<stdlib.h> #define DEBUG_OUT_OF_MEMORY static const size_t __ALIGN=8; static const size_t __MAX_BYTES=128; static const size_t __NFREELISTS=__MAX_BYTES/__ALIGN; class __default_alloc_template { private: //讲bytes上调至8的倍数 static size_t ROUND_UP(size_t bytes) { //高效率版本,没有用到乘法器,当比8多1时,就应该加倍9+7=16,多2时9+8=17,然后与1000000变成16,为8的整倍数 return (((bytes)+__ALIGN-1)&~(__ALIGN-1)); } //free_list节点构造 public: union obj { union obj* free_list_link; char client_data[1]; }; private: static obj *volatile free_list[__NFREELISTS]; //根据块大小,选择使用第n号的free_list,n从0开始 static size_t FREELIST_INDEX(size_t bytes) { return (((bytes)+__ALIGN-1)/__ALIGN-1); } public: static void *refill(size_t n); static char *chunk_alloc(size_t size,int &nobjs); public: //内存分配状态 static char *start_free; static char *end_free; static size_t heap_size; #ifdef DEBUG_OUT_OF_MEMORY static size_t nmalloc; #endif public: static void *allocate(size_t n) { obj *volatile *my_free_list; obj *result; //大于128调用第一级配置器,这里忽略大内存的处理 //在16个链表里面找到合适的free_list my_free_list=free_list+FREELIST_INDEX(n); result=*my_free_list; if(result==0) { void *r=refill(ROUND_UP(n)); return r; } //调整free_list,返回分配的空间,自由链表指向余下的空间首地址..会用到字节对齐? *my_free_list=result->free_list_link; printf("用掉自由链表%d的%d个字节的空间\n",FREELIST_INDEX(n)+1,n); return result; } static void *deallocate(void *p,size_t n) { } static void *reallocate(void *p,size_t old_sz,size_t new_sz) { } }; char *__default_alloc_template::start_free=0; char *__default_alloc_template::end_free=0; size_t __default_alloc_template::heap_size=0; #ifdef DEBUG_OUT_OF_MEMORY size_t __default_alloc_template::nmalloc=1; #endif __default_alloc_template::obj *volatile __default_alloc_template::free_list[__NFREELISTS]= {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; void *__default_alloc_template::refill(size_t n) { int nobjs=20; char *chunk=chunk_alloc(n,nobjs); obj *volatile *my_free_list; obj *result; obj *current_obj,*next_obj; int i; if(1==nobjs) //chunk_alloc函数里修改了nobjs return (chunk); my_free_list=free_list+FREELIST_INDEX(n); result=(obj*)chunk; *my_free_list=next_obj=(obj*)(chunk+n); //以下将free list的各个节点串联起来 for(i=1;;i++) { current_obj=next_obj; next_obj=(obj*)((char*)next_obj+n); if(nobjs-1==i) { current_obj->free_list_link=0; break; } else current_obj->free_list_link=next_obj; } } char *__default_alloc_template::chunk_alloc(size_t size,int &nobjs) { char *result; size_t total_bytes=size*nobjs; size_t bytes_left=end_free-start_free;//内存池剩余空间 if(bytes_left>=total_bytes) { result=start_free; start_free+=total_bytes; printf("内存池拨出%d个%d字节空间,总共%d字节给%d字节自由链表,内存池剩下%d字节空间\n",nobjs,size,nobjs*size,size,end_free-start_free); return (result); } else if (bytes_left>=size) { nobjs=bytes_left/size; total_bytes=size*nobjs; result=start_free; start_free+=total_bytes; printf("内存池拨出%d个%d字节空间,总共%d字节给%d字节自由链表,内存池剩下%d字节空间\n",nobjs,size,nobjs*size,size,end_free-start_free); return result; } else { //一个块大小的内存都没有了 //把内存池接到另外一块的头,不分配不使用任何空间 size_t bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4);//? if(bytes_left>0) { //内存池还有一些零头,把他接到另外一个链表的头那里 obj *volatile *my_free_list=free_list+FREELIST_INDEX(bytes_left); //剩余88字节讲移给第十一个自由链表 printf("内存池拨出%d字节给自由链表%d\n",bytes_left,FREELIST_INDEX(bytes_left)+1); ((obj*)start_free)->free_list_link=*my_free_list; *my_free_list=(obj*)start_free; } #ifdef DEBUG_OUT_OF_MEMORY if(nmalloc>0) { #endif start_free=(char*)malloc(bytes_to_get); printf("申请了%d字节的空间\n",bytes_to_get); nmalloc--; #ifdef DEBUG_OUT_OF_MEMORY } else start_free=0; #endif if(0==start_free) { //heap空间不足 int i; obj *volatile *my_free_list,*p; for(i=size;i<=__MAX_BYTES;i+=__ALIGN) { my_free_list=free_list+FREELIST_INDEX(i); p=*my_free_list; if(0!=p) { *my_free_list=p->free_list_link; printf("从自由链表%d挤出未用空间到内存池\n",i); start_free=(char*)p; end_free=start_free+i; //递归调用自己,修正nbojs return chunk_alloc(size,nobjs); } } end_free=0; printf("out of memory\n"); getchar(); } heap_size+=bytes_to_get; end_free=start_free+bytes_to_get; //递归调用自己,为了修正nobjs return chunk_alloc(size,nobjs); } } typedef __default_alloc_template alloc; //allocator包装类 template<class T,class Alloc> class simple_alloc { public: static T *allocate(size_t n) { return 0==n?0:(T*)Alloc::allocate(n*sizeof(T)); } //以下先省略... }; void main() { simple_alloc<int,alloc> data_allocator; data_allocator.allocate(2); data_allocator.allocate(8); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); data_allocator.allocate(2); }
结果
1、先分配20*8字节空间给自由链表#0,使用1个块,剩下19个块未使用。再用剩下的160字节给自由链表#3,使用1个块,剩下4个块未使用
2.把自由链表#0指向的内存使用完,模拟内存不足,此时内存已没有空间
3.这时要再分配8字节的空间,要递增查询自由链表是否还有可用空间,还给内存池
4.把挤出来的那部分内存给自由链表#0,使用1个块,剩下3个块
时间: 2024-10-05 08:31:53