Loading

2.内存管理的四步优化

主要讲述C++的内存分配的四步,即四个进化过程,不断封装的过程。
从定义一个指针next,构建简单的单向链表;到使用union共同体,即将一个东西的前四个字节作为指针来使用(嵌入式指针);到将内存分配与回收封装成allocator类,该类中包含两个成员函数allocate和deallocate,在其它类需要内存管理时,只需要将allocator声明为类成员变量,然后调用allocate和deallocate即可;最后将需要内存管理类中的两个函数实现封装为macro(宏定义),这样更加的方便。

内存管理第一步

在类中定义一个指针,形成单向链表,减少malloc的次数,即也是减少cookie

定义Screen类

namespace per_class_allocator {

	void per_class_allocator_function();
	class Screen {
	public:
		Screen(int x) : i(x) {};
		int get() { return i; }
		void*  operator new(size_t);
		void operator delete(void*, size_t);
	private:
		Screen * next;  //又多申请4个字节,膨胀百分百,降低malloc的次数,去除cookie,第一版多耗用一个next指针
		static Screen* freeStore;
		static const int screenChunk;
	private:
		int i;
	};
}

重载Screen类的operator new和operator delete

namespace per_class_allocator {
	Screen* Screen::freeStore = 0;
	const int Screen::screenChunk = 24;
	
	//重载Screen类的operator new
	void* Screen::operator new(size_t size) {
		Screen *p;
		if (!freeStore) {
			//linked list是空的,所以申请一大块
			size_t chunk = screenChunk * size;
			freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
			//将一大块分割片片,当做linked list串接起来
			for (; p = &freeStore[screenChunk - 1]; ++p)
				p->next = p + 1;
			p->next = 0;
		}
		p = freeStore;
		freeStore = freeStore->next;
		return p;
	}

	//重载Screen类的operator delete
	void Screen::operator delete(void *p, size_t) {
		//将deleted object插回free list前端
		(static_cast<Screen*>(p))->next = freeStore;
		freeStore = static_cast<Screen*>(p);
	}

开始测试

	//开始测试
	void per_class_allocator_function() {
		std::cout << sizeof(Screen) << std::endl; //8
		size_t const N = 10;
		Screen* p[N];

		for (int i = 0; i < N; ++i)
			p[i] = new Screen(i);

		//输出前10个pointers,比较其间隔
		for (int i = 0; i < 10; ++i)
			std::cout << p[i] << std::endl;

		for (int i = 0; i < N; ++i)
			delete p[i];
	}

内存管理第二步

定义Airplane

namespace per_class_allocator_two {
	void per_class_allocator_two_function();

	class Airplane {
	private:
		struct AirplaneRep {
			unsigned long miles;
			char type;
		};
	private:
		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;
		}
	public:
		static void* operator new(size_t size);
		static void operator delete(void* deadObject, size_t size);
	private:
		static const int BLOCK_SIZE;
		static Airplane* headOfFreeList;
	};
}

重载operator new和operator delete

	Airplane* Airplane::headOfFreeList;
	const int Airplane::BLOCK_SIZE = 512;
	
	//重载Airplane的operator new
	void* Airplane::operator new(size_t size) {
		if (size != sizeof(Airplane))
			return ::operator new(size);
		
		Airplane* p = headOfFreeList;
		if (p) //如果p有效,就把list的头部下移一个元素
			headOfFreeList = p->next;
		else {
			//free list 已空,申请分配一个大块内存
			Airplane* newBlock = static_cast<Airplane*>(::operator new(BLOCK_SIZE * sizeof(Airplane)));
			
			//再将每个小块串成一个free list,但是要跳过#0,它被传回,作为本次成果
			for (int i = 1; i < BLOCK_SIZE - 1; ++i)
				newBlock[i].next = &newBlock[i + 1];
			newBlock[BLOCK_SIZE - 1].next = 0; //结束list
			p = newBlock;
			headOfFreeList = &newBlock[1];
		}
		return p;
	}

	//重载Airplane类的operator delete
	//并没有还给操作系统,还是在这个自由链表中
	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 per_class_allocator_two_function() {
		std::cout  << sizeof(Airplane) << std::endl;
		size_t const N = 10;
		Airplane* p[N];

		for (int i = 0; i < N; ++i)
			p[i] = new Airplane;
		//随机测试object是否正常
		p[1]->set(1000, 'A');
		p[5]->set(2000, 'B');
		p[8]->set(500000, 'C');

		//输出10个pointers
		for (int i = 0; i < N; ++i)
			std::cout << p[i] << std::endl;
		for (int i = 0; i < N; ++i)
			delete p[i];
	}

测试结果

8
007312C8
007312D0
007312D8
007312E0
007312E8
007312F0
007312F8
00731300
00731308
00731310
请按任意键继续. . .

内存管理第三步

将所有的内存管理设计成一个类allocator,当在设计class时,不需要再重载operator new和operator delete.即分配特定尺寸的memory allocator概念封装起来。

每个allocator object都是一个分配器,它内部维护一个free lists,不同的allocator object维护不同的free lists. 集中到一个allocator中。

定义类allocator

	//设计allocator1类去封装malloc和delete
	class allocator1 {
	private:
		struct obj {
			struct obj*next; //嵌入式指针 embedded pointer
		};

	public:
		void* allocate(size_t);
		void deallocate(void*, size_t);
	private:
		obj * freeStore = nullptr;
		const int CHUNK = 5; //小一些以便观察
	};

定义分配内存和释放内存的函数allocate和deallocate

	//allocate的实现 类似于new
	void* allocator1::allocate(size_t size) {
		obj* p;
		if (!freeStore) {
			
			//linklist为空,申请一大块内存
			size_t chunk = CHUNK * size;
			freeStore = p = (obj*)malloc(chunk);
			
			//将分配得到的一大块当做linked list般,小块小块串接起来
			for (int i = 0; i < CHUNK - 1; ++i) {
				p->next = (obj*)((char*)p + size);
				p = p->next;
			}
			p->next = nullptr; //last node
		}
		p = freeStore;
		freeStore = freeStore->next;
		return p;
	}

	//allocator1的deallocate的首先
	void allocator1::deallocate(void*p, size_t size) {
		//将*p 收回插入到free list
		((obj*)p)->next = freeStore;
		freeStore = (obj*)p;
	}

开始测试

定义测试类Foo

	//实现Foo类去测试上面的allocator1类
	class Foo {
	public:
		long L;
		std::string str;
		static allocator1 myAlloc;
	public:
		Foo(long l) : L(l) {}
		
		//重载Foo类的operator new
		static void* operator new(size_t size) {
			return myAlloc.allocate(size);
		}
		//重载Foo类的operator delete
		static void operator delete(void* pdead, size_t size) {
			return myAlloc.deallocate(pdead, size);
		}
		
	};

定义测试函数

	allocator1 Foo::myAlloc;

	void static_allocator_function() {

		
		Foo *p[100]; //这里只是定义100个指针,并没有new分配内存
		std::cout << "sizeof(Foo)= " << sizeof(Foo) << std::endl;

		for (int i = 0; i < 23; ++i) {
			p[i] = new Foo(i);
			std::cout << p[i] << " " << p[i]->L << std::endl;
		}
		for (int i = 0; i < 23; ++i)
			delete p[i];

	}

测试结果

sizeof(Foo)= 32
007112C8 0
007112E8 1
00711308 2
00711328 3
00711348 4
00711398 5
007113B8 6
007113D8 7
007113F8 8
00711418 9
00711468 10
00711488 11
007114A8 12
007114C8 13
007114E8 14
00711D38 15
00711D58 16
00711D78 17
00711D98 18
00711DB8 19
00711E08 20
00711E28 21
00711E48 22
请按任意键继续. . .

内存管理第四步:macro for static allocator

将myAlloc转换为macro,只是为了偷懒,方便Foo直接调用。allocator类的定义和版本三完全一样,只不过是将Foo类中的下面两行抽出来定义为macro:

	//重载Foo类的operator new
		static void* operator new(size_t size) {
			return myAlloc.allocate(size);
		}
		//重载Foo类的operator delete
		static void operator delete(void* pdead, size_t size) {
			return myAlloc.deallocate(pdead, size);
		}

macro 定义

//将myAlloc.allocate转换为宏定义
	//DECLARE_POOL_ALLOC --used in class definition
	//用反斜杠来续行
#define DECLARE_POOL_ALLOC() \
public:\
	void* operator new(size_t size) { return myAlloc.allocate(size); } \
	void operator delete(void* p) { myAlloc.deallocate(p, 0); } \
protected: \
	static allocator1 myAlloc;

	//IMPLEMENT_POOL_ALLOC--used in class implementation file
#define IMPLEMENT_POOL_ALLOC(class_name) \
	allocator1 class_name::myAlloc;

头文件中Foo的定义(用宏又封装了一层)

	//定义用于测试的类 Foo
	class Foo {
		DECLARE_POOL_ALLOC()
	public:
		long L;
		std::string str;
	public:
		Foo(long l) :L(l) {}
	};

cpp中的实现

IMPLEMENT_POOL_ALLOC(Foo)
posted @ 2020-01-15 10:08  三只猫-  阅读(341)  评论(0编辑  收藏  举报