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第一部分空间配置器学习完毕,下一步将进行数据结构的复习,一级迭代器的学习。

 

 

<think>好的,我现在需要回答用户关于STL空间配置器的使用和实现细节的问题。用户可能已经看过之前的引用内容,但需要更深入的解释。首先,我要明确用户的需求:他们想了解空间配置器的具体使用方式以及实现细节,尤其是内部机制。 根据引用内容,空间配置器分为一级和二级,其中一级处理大块内存,直接使用malloc和free,而二级使用内存池和哈希桶管理小块内存。我需要解释这两级配置器的工作原理,以及它们如何协作。同时,用户可能对内存池的实现感兴趣,比如哈希桶的结构,内存块的分配和回收过程。 接下来,我需要考虑如何组织信息。首先介绍空间配置器的作用,然后分一级和二级详细说明。可能需要提到SGI-STL的实现,默认使用二级配置器,如何根据申请的内存大小切换级别。还要提到内存池的概念,哈希桶如何管理不同大小的内存块,比如8字节到128字节的倍数。 还要注意实现中的细节,比如空间配置器的效率优化,比如避免内存碎片,快速分配释放等。可能需要提到自由链表(free-list)的结构,每个桶节点如何连接空闲内存块,以及分配和回收时的操作步骤。 另外,用户可能对如何与容器结合使用感兴趣,比如vector或list如何调用空间配置器来分配内存。但引用中没有太多具体容器的例子,可能需要简要提及。 需要确保使用正确的中文术语,并按照用户的要求,行内公式用$...$,独立公式用$$...$$。不过当前问题主要涉及内存管理机制,可能不需要太多数学公式,但如果有涉及内存分配算法的地方,可能需要用公式表示。 检查引用内容,确保信息准确。例如,引用[3]提到超过128字节使用一级,小于则用二级。引用[4]说明二级使用内存池和哈希桶。引用[5]指出空间配置器为容器高效管理内存。 现在,组织回答的结构:首先概述空间配置器的定义和作用,然后分一级和二级的实现细节,接着讨论内存池和哈希桶的设计,最后可能涉及与容器的结合使用。最后生成相关问题。 在写作过程中,要引用对应的引用标记,比如当提到一级配置器时引用[3],提到内存池时引用[4]等。确保回答结构清晰,分点说明,帮助用户逐步理解。 可能需要注意避免技术术语过于晦涩,但用户要求的是实现细节,所以需要一定的技术深度。同时,代码示例可能有助于说明,但引用中没有提供具体代码,可能需要用文字描述流程。 总结:回答应包括一级和二级配置器的工作原理、内存池管理、哈希桶结构、内存分配与回收流程,以及如何被STL容器使用。确保引用正确,结构清晰,信息准确。</think>### STL空间配置器的使用与实现细节 STL空间配置器是容器进行内存管理的核心组件,主要解决**内存碎片**和**高频次内存申请**的问题。以下从**使用场景**和**实现原理**两个维度进行解析: --- #### 一、一级空间配置器 1. **适用场景** 当容器申请的内存块**大于128字节**时,直接使用一级空间配置器[^3]。 2. **实现原理** - 基于`malloc()`和`free()`实现,直接调用操作系统API管理内存。 - 提供`new_handler`机制处理内存不足问题:当`malloc()`失败时,尝试释放预留内存或抛出异常[^1]。 ```cpp // 伪代码示例 void* allocate(size_t size) { void* ptr = malloc(size); if (ptr == nullptr) { // 调用new_handler处理异常 throw_bad_alloc(); } return ptr; } ``` --- #### 二、二级空间配置器(核心实现) 1. **适用场景** 处理**小于等于128字节**的小内存块请求,通过内存池和自由链表(哈希桶)优化性能[^4]。 2. **内存池设计** - **哈希桶结构**:维护16个自由链表(8字节、16字节、...、128字节),每个链表管理特定大小的内存块[^4]。 - **内存块分配**: - 若对应链表有空闲块,直接返回第一个块。 - 若链表为空,从内存池批量申请20个块(可调整),将1个返回用户,剩余19个链入链表[^3]。 - **内存回收**:将释放的内存块重新链入对应链表,避免频繁调用`free()`[^4]。 $$ \text{内存块大小} = 8 \times (n+1) \quad (n \in [0,15]) $$ 3. **关键优化** - **减少内存碎片**:固定大小的内存块避免外部碎片。 - **提升分配速度**:链表操作时间复杂度为$O(1)$。 - **降低系统调用开销**:批量申请内存减少与操作系统的交互次数[^2]。 --- #### 三、空间配置器与容器结合 容器通过`Allocator`模板参数指定空间配置器: ```cpp template <class T, class Allocator = allocator<T>> class vector { // 使用Allocator分配内存 }; ``` - **默认配置**:STL容器默认使用`alloc`(二级空间配置器)[^3]。 - **对象构造/析构**:分离内存分配与对象初始化(`construct()`/`destroy()`)[^5]。 --- #### 、性能对比 | 场景 | 一级配置器 | 二级配置器 | |-------------------|---------------------|---------------------| | 大内存分配 | 直接高效 | 不适用 | | 小内存高频分配 | 内存碎片严重 | 内存池高效管理 | | 系统调用频率 | 高 | 低 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值