关于内存池,我相信大家都比较感兴趣,实现的版本也很多,但无论怎么实现,我觉得很重要的一点是:不能让管理内存池的成本太大!这是关键。比如,你管理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第一部分空间配置器学习完毕,下一步将进行数据结构的复习,一级迭代器的学习。