Просмотр исходного кода

feat: 新增两种内存分配的写法

liucong5 2 лет назад
Родитель
Сommit
68c0742427

BIN
cpp/img/Memory_04.png


BIN
cpp/img/Memory_05.png


BIN
cpp/img/Memory_06.png


BIN
cpp/img/Memory_07.png


BIN
cpp/img/Memory_08.png


+ 102 - 0
cpp/temp-cpp-proj/temp-cpp-proj/src/Airplane.cpp

@@ -0,0 +1,102 @@
+#include <cstddef>
+#include <iostream>
+
+class Airplane {
+private:
+	struct AirplaneRep {
+		unsigned long miles;
+		char type;
+	};
+
+private:
+	union {
+		AirplaneRep rep;	// 指针对使用中的 objects
+		Airplane* next;		// 指针对 fire list 中的object
+	};
+
+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;
+};
+
+Airplane* Airplane::headOfFreeList = nullptr;
+const int Airplane::BLOCK_SIZE = 512;
+
+void* Airplane::operator new(size_t size) {
+	// 如果大小有误 转交 ::operator new
+	if(size!=sizeof(Airplane)) {
+		return ::operator new(size);
+	}
+
+	Airplane* p = headOfFreeList;
+	if (p) {
+		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 = nullptr;
+		p = newBlock;
+		headOfFreeList = &newBlock[1];
+	}
+	return p;
+}
+
+void Airplane::operator delete(void* p, size_t size)
+{
+	if (p == nullptr) {
+		return;
+	}
+
+	if (size != sizeof(Airplane)) {
+		::operator delete(p);
+		return;
+	}
+
+	Airplane* carcass = static_cast<Airplane*>(p);
+	carcass->next = headOfFreeList;
+	headOfFreeList = carcass;
+}
+
+void foo_Ariplane() {
+	std::cout << sizeof(Airplane) << std::endl;
+
+	size_t const N = 100;
+	Airplane* p[N];
+
+	for (int i = 0; i < N; ++i) {
+		p[i] = new Airplane();
+	}
+
+	for (int i = 0; i < 10; ++i) {
+		std::cout << p[i] << std::endl;
+	}
+
+	for (int i = 0; i < N; ++i) {
+		delete p[i];
+	}
+}
+
+int main() {
+	foo_Ariplane();
+	return 0;
+}

+ 71 - 0
cpp/temp-cpp-proj/temp-cpp-proj/src/Screen.cpp

@@ -0,0 +1,71 @@
+#include <cstddef>
+#include <iostream>
+
+class Screen {
+public:
+	Screen(int x): i(x) {};
+	int get() { return i; }
+
+	static void* operator new(size_t);
+	static void operator delete(void*, size_t);
+
+private:
+	Screen* next;
+	static Screen* freeStore;
+	static const int screenChunk;
+
+private:
+	int i;
+};
+
+Screen* Screen::freeStore = nullptr;
+const int Screen::screenChunk = 24;
+
+void* Screen::operator new(size_t size)
+{
+	Screen* p = nullptr;
+	if (!freeStore) {
+		// linked list 为空 需要申请一大块内存
+		size_t chunk = screenChunk * size;
+		freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
+		// &freeStore[screenChunk - 1] 表示 freeStore 数组中最后一个 Screen 的内存地址 p 指向最后一个 Screen 时表示遍历结束
+		for (; p != &freeStore[screenChunk - 1]; ++p) {
+			p->next = p + 1;
+		}
+		p->next = nullptr;
+	}
+
+	p = freeStore;
+	freeStore = freeStore->next;
+	return p;
+}
+
+void Screen::operator delete(void* p, size_t)
+{
+	(static_cast<Screen*>(p))->next = freeStore;
+	freeStore = static_cast<Screen*>(p);
+}
+
+void foo_Screen() {
+	std::cout << sizeof(Screen) << std::endl;
+
+	size_t const N = 100;
+	Screen* p[N];
+
+	for (int i = 0; i < N; ++i) {
+		p[i] = new Screen(i);
+	}
+
+	for (int i = 0; i < 10; ++i) {
+		std::cout << p[i] << std::endl;
+	}
+
+	for (int i = 0; i < N; ++i) {
+		delete p[i];
+	}
+}
+
+//int main() {
+//	foo_Screen(); 
+//	return 0;
+//}

BIN
cpp/temp-cpp-proj/temp-cpp-proj/src/main.cpp


+ 2 - 0
cpp/temp-cpp-proj/temp-cpp-proj/temp-cpp-proj.vcxproj

@@ -127,7 +127,9 @@
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
+    <ClCompile Include="src\Airplane.cpp" />
     <ClCompile Include="src\main.cpp" />
     <ClCompile Include="src\main.cpp" />
+    <ClCompile Include="src\Screen.cpp" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">

+ 6 - 0
cpp/temp-cpp-proj/temp-cpp-proj/temp-cpp-proj.vcxproj.filters

@@ -18,5 +18,11 @@
     <ClCompile Include="src\main.cpp">
     <ClCompile Include="src\main.cpp">
       <Filter>源文件</Filter>
       <Filter>源文件</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="src\Screen.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Airplane.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 203 - 0
cpp/内存机制.md

@@ -411,3 +411,206 @@ int main() {
 }
 }
 ```
 ```
 
 
+### 分配内存
+
+```cpp
+Foo* p = new Foo();
+
+delete p;
+```
+
+上述代码等价于下面的代码,原理不再解释
+
+```cpp
+Foo* p = (Foo*)operator new(sizeof(Foo));
+new(p) Foo(x);
+
+p->~Foo();
+operator delete (p);
+```
+
+`operator new` 本质上还是调用 `malloc` 来分配内存,虽然这个过程一般来说很快,但是如果可以减少 `malloc` 调用的次数,一定程度上可以加快程序运行效率
+
+那么我们可以一开始就分配一个很大的内存,然后切成一个个小块分配给具体的对象,这样就只用 `malloc` 一次,然后我们手动对内存分块
+
+![](img/Memory_04.png)
+
+还有一点就是这样子可以减少 `cookie` 的量,每一次 `malloc` 除了分配指定大小的内存之外,还在内存块前后分别额外申请了内存用于存放 `cookie`,`cookie`中的信息用于表示内存**是否被分配**以及**分配的大小**等信息
+
+> `cookie` 并不是C++语言标准中的术语
+
+![](img/Memory_05.png)
+
+如果是一开始自己申请一大块内存,手动切割内存可以减少 `cookie` 的量,从而提升内存的使用率
+
+基于上面的想法,我们得到了如下的设计
+
+```cpp
+#include <cstddef>
+#include <iostream>
+
+class Screen {
+public:
+	Screen(int x): i(x) {};
+	int get() { return i; }
+
+	static void* operator new(size_t);
+	static void operator delete(void*, size_t);
+
+private:
+	Screen* next;
+	static Screen* freeStore;
+	static const int screenChunk;
+
+private:
+	int i;
+};
+
+Screen* Screen::freeStore = nullptr;
+const int Screen::screenChunk = 24;
+
+void* Screen::operator new(size_t size)
+{
+	Screen* p = nullptr;
+	if (!freeStore) {
+		// linked list 为空 需要申请一大块内存
+		size_t chunk = screenChunk * size;
+		freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
+		// &freeStore[screenChunk - 1] 表示 freeStore 数组中最后一个 Screen 的内存地址 p 指向最后一个 Screen 时表示遍历结束
+		for (; p != &freeStore[screenChunk - 1]; ++p) {
+			p->next = p + 1;
+		}
+		p->next = nullptr;
+	}
+
+	p = freeStore;
+	freeStore = freeStore->next;
+	return p;
+}
+
+void Screen::operator delete(void* p, size_t)
+{
+	(static_cast<Screen*>(p))->next = freeStore;
+	freeStore = static_cast<Screen*>(p);
+}
+```
+
+```cpp
+int main() {
+	std::cout << sizeof(Screen) << std::endl;
+
+	size_t const N = 100;
+	Screen* p[N];
+
+	for (int i = 0; i < N; ++i) {
+		p[i] = new Screen(i);
+	}
+
+	for (int i = 0; i < 10; ++i) {
+		std::cout << p[i] << std::endl;
+	}
+
+	for (int i = 0; i < N; ++i) {
+		delete p[i];
+	}
+	
+	return 0;
+}
+```
+
+在自定义 `opeator new` 和 `operator delete` 之前,连续申请100个 Screen,每个对象的地址信息,可见虽然是连续申请的对象,但是地址之间插值并不是 16,可见有 cookie 存在
+
+![](img/Memory_06.png)
+
+在使用自定义 `operator new` 和 `operator delete` 之后,Screen 对象地址连续,并没有被 Cookie 占用
+
+![](img/Memory_07.png)
+
+这里 Screen 使用了链表来连接整个大内存空间中的 Screen 对象
+
+有一个问题就是 Screen 对象中仅仅存在一个 `int i` 的属性大小是 8 个字节,但是为了自己分配内存空间需要额外定义 `Screen* next` 指针,指针也占用 8 个字节,浪费了 50% 的空间
+
+为了节省指针导致的内存浪费,可以使用 `union` 来处理
+
+```cpp
+class Airplane {
+private:
+	struct AirplaneRep {
+		unsigned long miles;
+		char type;
+	};
+
+private:
+	union {
+		AirplaneRep rep;	// 指针对使用中的 objects
+		Airplane* next;		// 指针对 fire list 中的object
+	};
+
+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;
+};
+
+Airplane* Airplane::headOfFreeList = nullptr;
+const int Airplane::BLOCK_SIZE = 512;
+
+void* Airplane::operator new(size_t size) {
+	// 如果大小有误 转交 ::operator new
+	if(size!=sizeof(Airplane)) {
+		return ::operator new(size);
+	}
+
+	Airplane* p = headOfFreeList;
+	if (p) {
+		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 = nullptr;
+		p = newBlock;
+		headOfFreeList = &newBlock[1];
+	}
+	return p;
+}
+
+void Airplane::operator delete(void* p, size_t size)
+{
+	if (p == nullptr) {
+		return;
+	}
+
+	if (size != sizeof(Airplane)) {
+		::operator delete(p);
+		return;
+	}
+
+	Airplane* carcass = static_cast<Airplane*>(p);
+	carcass->next = headOfFreeList;
+	headOfFreeList = carcass;
+}
+```
+
+`Airplane` 的 `sizeof` 大小为 8
+
+![](img/Memory_08.png)
+