关于内存池,我相信大家都比较感兴趣,实现的版本也很多,但无论怎么实现,我觉得很重要的一点是:不能让管理内存池的成本太大!这是关键。比如,你管理100M的内存需要花50M的空间才能搞定,那我觉得不如直接用malloc和free来得实在,除了管理内存消耗外,还要注意效率,如果效率太低那也没什么意义。
本质上,内存池技术的出现是为了减少fragment碎片问题,用malloc和free来操作内存容易造成内存区域的fragment,以至于大内存程序无法有效加载和运行,使用内存池技术,就是一次性申请很大的一段内存,然后由程序来管理内存的管理,这里很容易造成循环,就是程序代码的管理里面如果没有处理好,也会出现碎片问题,效率会很低而且没用。
基于内存管理的内存消耗和效率问题的考虑,参考了大量内存池实现版本之后,我觉得自己设计一个内存池,并且将这个内存池部署到我的HJSTL里面去,不仅如此,我将在以后的项目中大量使用内存池来管理我的内存。
说来惭愧,发现在《STL空间配置器(二)》里面的代码是有很多bug的,当时因为太过匆忙没来得及检测测试,后来今天测试的时候发现问题一大把,搞得我都没有兴趣看了....
不过这是我的HJSTL的空间配置器啊,不能不写啊,所以重拾信心,为了防止发生错误,严重依赖了SGI的空间配置器,我这样重写一遍代码的原因还有一个:学习内存池技术!
对,SGI空间配置器完美的展示了内存池技术的巧妙,这与内存池技术的初衷是一致的,内存池技术的出现很大程度是因为内存碎片问题,所以,在SGI中,它划定内存分配的粒度为8bytes,阈值为128bytes。超过128bytes则SGI认为不会造成内存碎片(我想也是,SGI应该是做了科学的统计才决定使用128bytes作为阈值的),所以内存池技术主要在第二级配置器发挥作用。
再三思考之后,我决定实现一个内存池,完全按照SGI空间配置器的逻辑与设计方法。然后用这个内存池作为HJSTL的空间配置器,我相信绰绰有余。
下面就是一个模仿SGI的内存池兼空间配置器。
/* * CopyRight (c) 2016 * HuJian in nankai edu. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * This file is about HJSTL's memory pool reference to SGI STL's allocate * Time :2016/4/7 in nankai edu */ #ifndef _HJSTL_MEMPOOL_ #define _HJSTL_MEMPOOL_ #include<iostream> #include<cstdio> #include<cstdlib> //#define HJSTL_DEBUG //for debug using namespace std; #define __HJSTL_PRIVATE_ private #define __HJSTL_PUBLIC_ public #define __HJSTL_BAD_ALLOC_ cout<<"out of memory"<<endl; exit(1) #define __HJSTL_MIN_ALLOC_ 4 //the min align size is 4 bytes #define __HJSTL_MAX_ALLOC_ 1024 //the max align size is 1024 bytes<1Mb> #define __HJSTL_NFREELISTS_ (__HJSTL_MAX_ALLOC_/__HJSTL_MIN_ALLOC_) //the free list node structure //the user_data is funny,the memory will boot by self one by one. //think of it yourself carefully.it's very funny and i laugh at myself. union hjstl_free_list_node{ union hjstl_free_list_node *free_list_link; char user_data[1]; }; //this is the free_list array,tell compile not opt this array hjstl_free_list_node* volatile hjstl_free_list[__HJSTL_NFREELISTS_]; //and the memory pool's size char* hjstl_memory_pool_start=0; char* hjstl_memory_pool_end=0; size_t hjstl_memory_pool_heap_size=0; //*this is the first level allocate.and the allocate handler //the memory>128bytes,and others job handle by second level allocate //the real-memory-pool is the second level allocate. template<int inst> class _hjstl_first_level_memory_pool_allocate{ __HJSTL_PRIVATE_://you should not touch this part code.<hujian> static void* hjstl_oom_malloc(size_t); static void* hjstl_oom_realloc(void*, size_t); //the memory also contain two level allocate,the first level allocate //handle the memory bigger 128bytes,and the second level allocate handle //the less 128bytes memory ask. //you also can set an new-handler to solve the out-of-memory //i will give the API to set/check the handler static void(*__hjsetl_malloc_oom_handler)(); __HJSTL_PUBLIC_: static void * hjstl_allocate(size_t size) { void* result = malloc(size); if (0 == result) result = hjstl_oom_malloc(size); return result; } static void hjstl_deallocate(void* mem, size_t) { free(mem); } static void* hjstl_reallocate(void* oldmem, size_t oldsz, size_t newsz) { void* result = realloc(oldmem, newsz); if (0 == result) result = hjstl_oom_realloc(oldmem, newsz); return result; } //you can set your oom-handler by this function,return the old handler static void(*hjstl_set_malloc_oom_handler(void(*handler)()))() { void(*old)() = __hjsetl_malloc_oom_handler; __hjsetl_malloc_oom_handler = handler; return(old); } //chenck whether we install the oom handler static bool hjstl_whether_set_oom_handler() { return (__hjsetl_malloc_oom_handler == 0 ? false : true); } }; //implement the function of first level. template<int inst> void* _hjstl_first_level_memory_pool_allocate<inst>::hjstl_oom_malloc(size_t size) { void(*my_malloc_oom_handler)(); void* result; for (;;){ my_malloc_oom_handler = __hjsetl_malloc_oom_handler; if (0 == my_malloc_oom_handler) { __HJSTL_BAD_ALLOC_; } //run your oom-handler (*my_malloc_oom_handler)(); result = malloc(size); //re-try if (result) return result; } } template<int inst> void* _hjstl_first_level_memory_pool_allocate<inst>::hjstl_oom_realloc(void* oldmem, size_t newsz) { void(*my_malloc_oom_handler)(); void* result; for (;;){ my_malloc_oom_handler = __hjsetl_malloc_oom_handler; if (0==my_malloc_oom_handler){ __HJSTL_BAD_ALLOC_; } //run your handler (*my_malloc_oom_handler)(); result = realloc(oldmem, newsz); if (result) return result; } } template<int inst> void(*_hjstl_first_level_memory_pool_allocate<inst>::__hjsetl_malloc_oom_handler)()=0; //you can use this in your test-project typedef _hjstl_first_level_memory_pool_allocate<0> hjstl_first_malloc; //this is the second level allocate of hjstl memory pool template<int inst> class _hjstl_secnod_level_memory_pool_allocate{ //you can not touch this part's code,just for root __HJSTL_PRIVATE_: //round up to align 8 times bytes static size_t HJSTL_ROUND_UP(size_t bytes){ return (((bytes)+__HJSTL_MIN_ALLOC_ - 1) & ~(__HJSTL_MIN_ALLOC_ - 1)); } //find the free list,return the index static size_t HJSTL_FREELISTS_INDEX(size_t bytes){ return (((bytes)+__HJSTL_MIN_ALLOC_ - 1) / __HJSTL_MIN_ALLOC_ - 1); } //return an node of size sz,and the left nodes append to the free list //so,after call this function,the free list maybe change<update> static void* hjstl_refill(size_t sz); //Allocate a chunk from os,nnodes maybe reduce if the memory pool no enough mem static char* hjstl_mem_pool_chunk_alloc(size_t sz, int &Nnodes); __HJSTL_PUBLIC_://you can use this api in your project //allocate memory,but this function just handle the memory less 128. //if the memory bigger 128,call first levle's allocate to handle it. static void* hjstl_allocate(size_t sz) { hjstl_free_list_node* volatile * my_free_list; hjstl_free_list_node* result; if (sz > (size_t)__HJSTL_MAX_ALLOC_){ return (hjstl_first_malloc::hjstl_allocate(sz)); } //get the aim-free list my_free_list = hjstl_free_list + HJSTL_FREELISTS_INDEX(sz); result = *my_free_list; if (0 ==result){//no,this size's free list is empty,ok,re-fill it<20 nodes> void* r = hjstl_refill(sz); #ifdef HJSTL_DEBUG cout << "hjstl_refill calling..." << endl; #endif return (r); } //else,this type's free list is not empty,so,give an node to user //and update the free list. *my_free_list = result->free_list_link; return (result); } //deallocate,i don't know how to real-know the kernel of free... static void hjstl_deallocate(void* mem, size_t sz) { hjstl_free_list_node* volatile* my_free_list; hjstl_free_list_node* append_link_node=(hjstl_free_list_node*)mem; //dispatch it to first level deallocate if the memory size bigger [128] bytes if (sz > (size_t)__HJSTL_MAX_ALLOC_){ hjstl_first_malloc::hjstl_deallocate(mem, sz); return; } //else,release this mem,and give it to free list. //append it front of free list. my_free_list = hjstl_free_list + HJSTL_FREELISTS_INDEX(sz); append_link_node->free_list_link = *my_free_list; *my_free_list = append_link_node; } //you cant to re-allocate memory?use this function static void* hjstl_reallocate(void* oldmem, size_t oldsz, size_t newsz); #ifdef HJSTL_DEBUG //check the free list ,return the size's free list's node num static size_t debug_hjstl_getnodesnum(size_t sz) { size_t result = 0; hjstl_free_list_node* volatile* my_free_list; hjstl_free_list_node* p; my_free_list = hjstl_free_list + HJSTL_FREELISTS_INDEX(sz); p = *my_free_list; while (p != 0){ result++; p = p->free_list_link; } cout << "The size " << sz << " free list nodes num is:" << result << endl; return result; } //the left memory of memory pool static size_t debug_hjstl_leftmem() { cout << "The left memory of memory pool is:" << (hjstl_memory_pool_end - hjstl_memory_pool_start) << endl; return (hjstl_memory_pool_end - hjstl_memory_pool_start); } #endif }; //implement the second level memory pool. //this is the memory pool,and return an node for user,maybe //update the free list but maybe not update. template<int inst> char* _hjstl_secnod_level_memory_pool_allocate<inst>::hjstl_mem_pool_chunk_alloc(size_t sz, int& Nnodes) { char* result; size_t total_bytes_to_alloc = sz*Nnodes; //the left memory of memory pool size_t left_bytes_mempool = hjstl_memory_pool_end - hjstl_memory_pool_start; //case 1,if the memory's memory is enough,just allocate.change the memory pool if (left_bytes_mempool >= total_bytes_to_alloc){ result = hjstl_memory_pool_start; hjstl_memory_pool_start += total_bytes_to_alloc; return (result); } //case 2,check whether the left bytes reah 1 nodes of this size,if true,return //the actual nodes we can give. else if (left_bytes_mempool >= sz){ //calc how many nodes we can allocate Nnodes = left_bytes_mempool / sz; //change the memory pool total_bytes_to_alloc = Nnodes*sz; result = hjstl_memory_pool_start; hjstl_memory_pool_start += total_bytes_to_alloc; return (result); } //case 3,ok,we find the free list not memory<maybe>,so,call first level allocate //to malloc a big chunk from os,and continue to allocate. else{ size_t bytes_from_os = 8* total_bytes_to_alloc + HJSTL_ROUND_UP(hjstl_memory_pool_heap_size >> 4); //whether old pool left some fragments? if (left_bytes_mempool > 0){ hjstl_free_list_node* volatile* my_free_list = hjstl_free_list + HJSTL_FREELISTS_INDEX(left_bytes_mempool); ((hjstl_free_list_node*)hjstl_memory_pool_start)->free_list_link = *my_free_list; *my_free_list = (hjstl_free_list_node*)hjstl_memory_pool_start; } //reboot the memory pool hjstl_memory_pool_start = (char*)hjstl_first_malloc::hjstl_allocate(bytes_from_os); //if the first level memory ask error<oom?> if (0 == hjstl_memory_pool_start){ //this is the last way to allocate.check others free list,and //release some others nodes,and re-try... hjstl_free_list_node* volatile* my_free_list, *find_pointer; for (int i = sz; i <= __HJSTL_MAX_ALLOC_; i += __HJSTL_MIN_ALLOC_){ my_free_list = hjstl_free_list + HJSTL_FREELISTS_INDEX(i); find_pointer = *my_free_list; if (0 != find_pointer){ *my_free_list = find_pointer->free_list_link; hjstl_memory_pool_start = (char*)find_pointer; hjstl_memory_pool_end += i; //retry till get enough memory,the nnodes will reduce after run sometimes return (hjstl_mem_pool_chunk_alloc(sz, Nnodes)); } } }//end of oom //adjust the memory pool,and re-try hjstl_memory_pool_heap_size += bytes_from_os; hjstl_memory_pool_end = hjstl_memory_pool_start + bytes_from_os; return (hjstl_mem_pool_chunk_alloc(sz, Nnodes)); } } //refill the free list.maybe 20nodes,maybe none template<int inst> void* _hjstl_secnod_level_memory_pool_allocate<inst>::hjstl_refill(size_t sz) { int default_nodes = 20; //you can change the num char* alloc_chunk = hjstl_mem_pool_chunk_alloc(sz, default_nodes); hjstl_free_list_node* volatile * my_free_list; hjstl_free_list_node* result, *current_p, *next_p; //if the memory just return 1 nodes,ok,return it and kill self.. if (1 == default_nodes) return (alloc_chunk); //else,update the free list. my_free_list = hjstl_free_list + HJSTL_FREELISTS_INDEX(sz); //anyway,we need to give user one nodes. result = (hjstl_free_list_node*)alloc_chunk; //|*connect the nodes *| *my_free_list = next_p = (hjstl_free_list_node*)(alloc_chunk + sz); for (int i = 1;; i++){ current_p = next_p; next_p = (hjstl_free_list_node*)((char*)next_p + sz); if (default_nodes - 1 == i){//this is the last node current_p->free_list_link = 0; break; } else{//this is not the last node,pointer to next current_p->free_list_link = next_p; } } //everything done,return the result. return (result); } //re-allocate template<int inst> void* _hjstl_secnod_level_memory_pool_allocate<inst>::hjstl_reallocate(void* oldmem, size_t oldsz, size_t newsz) { void* result; size_t copy_size; if (oldsz>(size_t)__HJSTL_MAX_ALLOC_&&newsz >(size_t)__HJSTL_MAX_ALLOC_){ return (hjstl_first_malloc::hjstl_reallocate(oldmem, oldsz, newsz)); } //if same,just return if (HJSTL_ROUND_UP(newsz) == HJSTL_ROUND_UP(oldsz)) return (oldmem); //else,we use the second level allocate to do this job result = hjstl_allocate(newsz); //you should know if the newsz<oldsz,the data will lose. copy_size = newsz > oldsz ? oldsz : newsz; memcpy(result, oldmem, copy_size); //release the old memory hjstl_deallocate(oldmem, oldsz); return (result); } //you can use this in your project typedef _hjstl_secnod_level_memory_pool_allocate<0> hjstl_second_malloc; typedef hjstl_second_malloc hjstl_memory_pool; #endif //end of hjstl mempool
相应的,hjst空间配置器的部署就相当简单,见下面。
/* * CopyRight (c) 2016 * HuJian in nankai edu. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * This file is about HJSTL's memory allocate reference to SGI STL's allocate * Time :2016/4/5 in nankai edu */ #ifndef _HJ_STL_ALLOC_H_ #define _HJ_STL_ALLOC_H_ #include "hjstlMemPool.h" #define HJSTL_VERSION "hjstl version 1.0.1,hujian,copyright C 2016" //ok,in this file,we do nothing but define the alloc,for some reason,i will //define some allocate,you can use the follow allocates when you need. template<int inst> class ___hjstl_allocate_{ public: //you can use the follow allocate.the follow allocate will use in hjstl typedef hjstl_first_malloc Alloc_first; typedef hjstl_second_malloc Alloc_second; typedef hjstl_first_malloc Alloc_user_first; typedef hjstl_second_malloc Alloc_user_second; //you should not use the follow allocate.the follow allocate will use in hjstl typedef hjstl_first_malloc HJSTL_Allocate; typedef hjstl_second_malloc HJSTL_Allocate_default; typedef HJSTL_Allocate_default Alloc; }; //ok,you can use this in your project typedef ___hjstl_allocate_<0> HJSTL_Alloc; #endif //end of _HJ_STL_ALLOC_H_
至此,STL第一部分空间配置器学习完毕,下一步将进行数据结构的复习,一级迭代器的学习。