C++内存管理:内存池实现

示例1:在类中提前分配一块连续的内存池,减少cookie(分配内存时产生的头尾共8个字节,用于记录分配了多少内存)对内存的消耗

class Screen {
public:
	Screen(int x) : i(x) {}
	int getData() { return i; }
	void* operator new(size_t);
	void operator delete(void*, size_t);
private:
	static Screen* freeStore;//对象的内存地址
	static const int screenChunk;//要获取的Screen类的个数,内存数量 = sizeof(Screen) * screenChunk
	Screen* next;
	int i;
};
Screen* Screen::freeStore = nullptr;
const int Screen::screenChunk = 4;

void* Screen::operator new(size_t size){
	Screen* p;
	//freeStore不为空表示预先拿到的内存还有位置
	if (!freeStore) {
		size_t  chunk = size * screenChunk;
		freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
		for (; p != &freeStore[screenChunk - 1]; ++p)
			p->next = p + 1;
		p->next = 0;
	}
	p = freeStore;
	freeStore = freeStore->next;//每调用一块内存往后移一位
	return p;
}

void Screen::operator delete(void* p, size_t size) {
	static_cast<Screen*>(p)->next = freeStore;
	freeStore = static_cast<Screen*>(p);
}

void test() {
	cout << "sizeof Screen: " << sizeof(Screen) << endl;//获取Screen大小
	Screen* p[100];
	for (int i = 0; i < 100; i++) {
		p[i] = new Screen(i);     //创建
	}
	for (int i = 0; i < 100; i++) {
		cout << "address: " << p[i] << "   i = " << p[i]->getData() << endl;
	}
	for (int i = 0; i < 100; i++) {
		delete p[i];              //销毁
	}
}

结果:screenChunk取值为4,所以每4个Screen对象的空间是连续的

 示例2:例1的优化版,加入embedded pointer

class Airplane {
private:
	struct AirplaneRep {
		unsigned long miles;
		char type;
	};
	union {
		AirplaneRep rep;
		Airplane* next;
	};
public:
	unsigned long getMiles() { return rep.miles; }
	char getType() { return rep.type; }
	void set(unsigned long m, char t) {
		rep.miles = m;
		rep.type = t;
	}
	
	void* operator new(size_t size);
	void operator delete(void*, size_t);
private:
	static Airplane* headOfFreeList;
	static const int BLOCK_SIZE;
};
Airplane* Airplane::headOfFreeList = nullptr;
const int Airplane::BLOCK_SIZE = 512;

void* Airplane::operator new(size_t size){
	if (size != sizeof(Airplane)) {
		return ::operator new(size);
	}	
	Airplane* p = headOfFreeList;
	if (p) headOfFreeList = p->next;
	else {
		size_t total_size = size * BLOCK_SIZE;
		Airplane* newBLOCK = reinterpret_cast<Airplane*>(new char[total_size]);
		for (int i = 1; i < BLOCK_SIZE-1; i++)
			newBLOCK[i].next = &newBLOCK[i + 1];
		newBLOCK[BLOCK_SIZE - 1].next = 0;
		p = newBLOCK;
		headOfFreeList = &newBLOCK[1];
	}
	return p;
}

void Airplane::operator delete(void* deadObject, size_t size) {
	if (deadObject == 0) return;
	if (size != sizeof(Airplane)) {
		::operator delete(deadObject);
		return;
	}
	Airplane* carcass = static_cast<Airplane*>(deadObject);
	carcass->next = headOfFreeList;
	headOfFreeList = carcass;
}



void test() {
	const int size = 100000;
	cout << "sizeof Airplane: " << sizeof(Airplane) << endl;//获取Screen大小
	Airplane* p[size];
	for (int i = 0; i < size; i++) {
		p[i] = new Airplane();
		p[i]->set(i, i % 3);
	}
	for (int i = 0; i < size; i++) {
		cout << "address: " << p[i] << "   i = " << p[i]->getMiles() << endl;
	}
	for (int i = 0; i < size; i++) {
		delete p[i];//销毁
	}
}

示例3:采用一个类来进行内存池,让所有类都能共用

 示例4:加入宏定义

示例5:标准库pool_allocator的实现

//******** pool_allocator 设计 ********

enum { __ALIGN = 8 };
enum { __MAX_BYTES = 128 };
enum { __NRFEELISTS = __MAX_BYTES / __ALIGN };
template<bool threads, int inst>
class __default_alloc_template {
private:
	//向上取8倍数
	static size_t ROUND_UP(size_t bytes) {
		return (((bytes) +__ALIGN - 1) & ~(__ALIGN - 1));
	}
private:
	union obj {
		union obj* free_list_link;
	};
private:
	static obj* volatile free_list[__NRFEELISTS];//存入16条free_list的头
	static size_t FREELIST_INDEX(size_t bytes) { //根据obj大小找到对应的free_list索引
		return (((bytes) +__ALIGN - 1) / __ALIGN - 1);
	}
	//分配内存池中的内存
	static void* refill(size_t n);
	//向系统获取内存放入内存池
	static void* chunk_alloc(size_t size, int& nobjs);

	static char* start_free;   //内存池起点
	static char* end_free;     //内存池终点
	static size_t heap_size;   //从系统中获取的内存大小

public:
	static void* allocate(size_t n) {
		obj* volatile *my_free_list;
		obj* result;
		if (n > __MAX_BYTES) {
			return ::operator new(n);
		}
			
		my_free_list = free_list + FREELIST_INDEX(n);
		result = *my_free_list;
		if (0 == result) {
			void* r = refill(ROUND_UP(n));
			return r;
		}
		*my_free_list = result->free_list_link;
		return result;
	}
	static void deallocate(void* p, size_t n) {
		obj* q = (obj*) p;
		obj* volatile *my_free_list;
		if (n > __MAX_BYTES) {
			::operator delete(q, n);
			return;
		}
		my_free_list = free_list + FREELIST_INDEX(n);
		q->free_list_link = *my_free_list;
		*my_free_list = q;
	}
	//先不实现
	static void* reallocator(void* p, size_t old_sz, size_t new_sz);
};

template<bool threads, int inst>
void* __default_alloc_template<threads, inst>::
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;
		return result;
	}
	//内存池够一个或以上的obj大小
	else if(bytes_left >= size){
		nobjs = bytes_left / size;
		total_bytes = nobjs * size;
		result = start_free;
		start_free += total_bytes;
		return result;
	}
	//内存池连一个都obj大小都分不出来,先把剩余的内存分出去,再向系统要内存
	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);
			((obj*) start_free)->free_list_link = *my_free_list;
			*my_free_list = (obj*) start_free;
		}
		//向系统要内存
		start_free = (char*) malloc(bytes_to_get);
		//start_free == 0 意味着系统分不出这么多内存了,需要向free_list里比size大的list要内存
		if (0 == start_free) {
			size_t i;
			obj* volatile *my_free_list;
			obj* p;
			for (i = size; i <= __MAX_BYTES; i + __ALIGN) {
				my_free_list = free_list + FREELIST_INDEX(i);
				p = *my_free_list;
				//要到了
				if (nullptr != p) {
					*my_free_list = p->free_list_link;
					start_free = (char*) p;
					end_free = start_free + i;
					return chunk_alloc(size, nobjs);
				}
			}
			//没要到,程序跑到这里应该会陷入死循环
			end_free = 0;
			start_free = (char*) ::operator new(bytes_to_get);
		}
		heap_size += bytes_to_get;
		end_free = start_free + bytes_to_get;
		return chunk_alloc(size, nobjs);
	}
}

template<bool threads, int inst>
void* __default_alloc_template<threads, inst>::
refill(size_t n) {
	int nobjs = 2;//一般为20,为方便实验,每次取2块obj的大小
	char* chunk = (char*) chunk_alloc(n, nobjs);
	obj* volatile *my_free_list;
	obj*  result;
	obj* current_obj;
	obj* next_obj;
	int i;
	if (1 == nobjs) return chunk;
	my_free_list = free_list + FREELIST_INDEX(n);
	//在chunk内建立free_list
	result = (obj*) chunk;
	*my_free_list = next_obj = (obj*) (chunk + n);
	for (i = 1; ; ++i) {
		current_obj = next_obj;
		next_obj = (obj*) ((char*)current_obj + n);
		if (i == nobjs - 1) {
			current_obj->free_list_link = 0;
			break;
		}
		else {
			current_obj->free_list_link = next_obj;
		}
	}
	return result;
}

template<bool threads, int inst>
char* __default_alloc_template<threads, inst>::start_free = 0;
template<bool threads, int inst>
char* __default_alloc_template<threads, inst>::end_free = 0;
template<bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;
template<bool threads, int inst>
typename __default_alloc_template<threads, inst>::obj* volatile
__default_alloc_template<threads, inst>::free_list[__NRFEELISTS]
	= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

typedef __default_alloc_template<false, 0> myPoolAlloc;

void test() {
	myPoolAlloc alloc;
	int* pi[6];
	//new
	for (int i = 0; i < 6; ++i) {
		pi[i] = (int*) alloc.allocate(16);
		cout << pi[i] << endl;//打印地址
	}	
	//delete
	for (int i = 0; i < 6; ++i) {
		alloc.deallocate(pi[i],16);
	}
}

结果:每次取2个区块,第一次内存池获取两倍的所需内存,所以应该有四块内存连续,结果正确

size_t bytes_to_get = 2 * total_bytes + (ROUND_UP(heap_size >> 4));

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值