STL配置器(四)----内存池技术

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

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值