在程序设计领域,程序员经常需要及时动态地产生出一些小型对象,例如读取解析文件时临时需要的缓冲区,动态创建视图时需要的视图对象,游戏程序中怪物,特效,场景物乃至于低级的链表节点等等。如果程序员只是天真地使用new和delete,在经过程序执行中不断反复的分配释放内存后,很快就会使原本完整连续的可用内存块变得支离破碎,这就是内存碎片即Memory Fragmentation问题。更糟的是内存碎片问题往往很难检测出来。
为了尽可能降低内存碎片的状况,并且得到高效的内存管理程序,除了从增加计算机配置,如增加内存条,和从程序的逻辑上着手,如减少读取解析文件时临时需要的缓冲区,减少动态创建视图的次数,减少游戏程序中怪物,特效,场景物出现频度之外,同时还需要从程序的低级层面,即从内存分配的功能面着手改善。
在用户每次new操作从系统索取内存空间时,C++的函数库除了配置所要求的内存块大小以外,还会另外生成一小块额外的内存块以记载相关的资料。对于经常进行产生与销毁的小型对象来说,这样的行为就显得十分浪费而不具效率。如果能够一次性的分配出一大块内存,然后再根据用户的需求传回部分内存块,这样就能改善new操作所产生的额外负担。
在这里,我参考早年微软MSJ期刊的一些资料写出了一个内存池C++类:CMemPool,这个内存池建立在树状的双向链表之上的,其相关结构体定义如下:
1. //树型内存块分配结构.
2. typedef struct
3. struct
4. {
5. DWORD
6. DWORD
7. LPMEMORY_BLOCK lpNext;
8. LPMEMORY_BLOCK lpPrev;
9. LPBYTE
10. };
11.
12. //内存池标头结构
13. typedef struct
14. struct
15. {
16. LPMEMORY_BLOCK lpHead;
17. HANDLE
好了,我就不再啰嗦了,下面列出CMemPool类的源码,有不对之处,请不吝赐教!~~~
CMemPool类的头文件MemPool.h代码如下:
1. //=====================================================================
2. // 内存池类CMemPool的头文件CMemPool.h
3. //by 一剑(Loomman),QQ:28077188,MSN: Loomman@hotmail.com QQ裙:30515563
4. //=====================================================================
5. #ifndef MEMPOOL_H
6. #define MEMPOOL_H
7.
8. #define STRICT
9. #define LEAN_AND_MEAN
10.
11. #include <windows.h>
12. #include <assert.h>
13. #include <stdio.h>
14. #include <mbstring.h>
15.
16. //默认内存分配页大小设为8KB
17. #define MEMPOOL_BLOCK_SIZE 8192
18.
19. class
20. {
21. public:
22.
23. CMemPool();
24. ~CMemPool();
25.
26. BOOL
27. VOID
28.
29. LPVOID GetAlignedMemory(DWORD dwSize, DWORD
30. LPSTR GetDuplicateStringA(LPCSTR
31. LPWSTR GetDuplicateStringW(LPCWSTR
32.
33. inline LPVOID GetMemory(DWORD
34. {
35. return
36. }
37.
38. inline TCHAR* GetStringBuffer(DWORD
39. {
40. return (TCHAR*)GetAlignedMemory(dwLen * sizeof(TCHAR), 0);
41. }
42.
43. inline LPSTR GetStringBufferA(DWORD
44. {
45. return (LPSTR)GetAlignedMemory(dwLen * sizeof(CHAR), sizeof(CHAR));
46. }
47.
48. inline LPWSTR GetStringBufferW(DWORD
49. {
50. return (LPWSTR)GetAlignedMemory(dwLen * sizeof(WCHAR), sizeof(WCHAR));
51. }
52.
53. inline DWORD* GetDwordBuffer()
54. {
55. return (DWORD*)GetAlignedMemory(sizeof(DWORD), 0);
56. }
57.
58. private:
59. BOOL AddMemory(DWORD
60. LPVOID
61. };
62.
63. #endif //MEMPOOL_H
CMemPool类的实现文件MemPool.cpp代码如下:
1. //=====================================================================
2. // 内存池类CMemPool的实现文件CMemPool.cpp
3. //by 一剑(Loomman),QQ:28077188,MSN: Loomman@hotmail.com QQ裙:30515563
4. //=====================================================================
5. #include "MemPool.h"
6.
7. //树型内存块分配结构.
8. typedef struct
9. struct
10. {
11. DWORD
12. DWORD
13. LPMEMORY_BLOCK lpNext;
14. LPMEMORY_BLOCK lpPrev;
15. LPBYTE
16. };
17.
18. //内存池标头结构
19. typedef struct
20. struct
21. {
22. LPMEMORY_BLOCK lpHead;
23. HANDLE
24. };
25.
26. //内存池对象构造函数
27. CMemPool::CMemPool()
28. {
29. handle = NULL;
30. }
31.
32. //内存池对象析构函数
33. CMemPool::~CMemPool()
34. {
35. Destroy();
36. }
37.
38. //内存池对象初始化,首次分配8KB内存作为内存池
39. BOOL
40. {
41. if( NULL == handle )
42. {
43. HANDLE
44.
45. // 分配内存池的头节点.
46. sizeof(HEADER));
47.
48. if
49. {
50. LPHEADER header = (LPHEADER)handle;
51. // 分配头节点成功,现在初始化内存池.
52. header->lpHead = NULL;
53. header->hHeap = procHeap;
54.
55. //初次实际分配8KB内存到内存池.
56. BOOL
57.
58. if
59. {
60. //分配内存失败,系统资源瓶颈!
61. HeapFree(header->hHeap, 0, handle);
62. handle = NULL;
63. }
64. }
65. }
66. return
67. }
68.
69. VOID
70. {
71. if(handle != NULL)
72. {
73. LPMEMORY_BLOCK nextBlock;
74. LPMEMORY_BLOCK blockToFree;
75. LPHEADER poolHeader = (LPHEADER)handle;
76.
77. //遍历链表,进行释放内存操作.
78. blockToFree = poolHeader->lpHead;
79.
80. while
81. {
82. nextBlock = blockToFree->lpNext;
83. HeapFree(poolHeader->hHeap, 0, blockToFree);
84. blockToFree = nextBlock;
85. }
86.
87. //别忘记了,内存池头结点也需要释放.
88. HeapFree(poolHeader->hHeap, 0, handle);
89. handle = NULL;
90. }
91. }
92.
93. LPVOID CMemPool::GetAlignedMemory(DWORD dwSize, DWORD
94. {
95. assert(handle != NULL);
96.
97. BOOL
98. LPVOID
99. LPHEADER poolHeader = (LPHEADER)handle;
100. LPMEMORY_BLOCK currentBlock;
101. DWORD
102. DWORD
103.
104. currentBlock = poolHeader->lpHead;
105.
106. // 判断是否需要更多的内存,如果是,则分配之.
107. sizeNeeded = dwSize;
108.
109. if
110. {
111. haveEnoughMemory = AddMemory(sizeNeeded + dwAlignSize);
112. currentBlock = poolHeader->lpHead;
113. }
114.
115. // 现在有了足够的内存,返回它!
116. if
117. {
118. if
119. {
120. DWORD)currentBlock + sizeof(MEMORY_BLOCK) + currentBlock->dwIndex;
121. currentBlock->dwIndex += (dwAlignSize - (padLength % dwAlignSize)) % dwAlignSize;
122. }
123.
124. //这里得到了内存地址,返回它!
125. LPVOID)&(currentBlock->lpMemory[currentBlock->dwIndex]);
126.
127. currentBlock->dwIndex += sizeNeeded;
128. }
129.
130. return
131. }
132.
133. LPSTR CMemPool::GetDuplicateStringA(LPCSTR
134. {
135. assert(szSrc);
136.
137. DWORD dwBytes = (_mbslen((const unsigned char*)szSrc) + 1) * sizeof(CHAR);
138. LPSTR pString = (LPSTR)GetAlignedMemory(dwBytes, sizeof(CHAR));
139.
140. if
141. {
142. char*)pString, dwBytes, (const unsigned char*)szSrc);
143. }
144. return
145. }
146.
147. LPWSTR CMemPool::GetDuplicateStringW(LPCWSTR
148. {
149. assert(szSrc);
150.
151. DWORD dwBytes = (wcslen(szSrc) + 1) * sizeof(WCHAR);
152. LPWSTR pString = (LPWSTR)GetAlignedMemory(dwBytes, sizeof(WCHAR));
153.
154. if
155. {
156. wcscpy_s(pString, dwBytes, szSrc);
157. }
158. return
159. }
160.
161. BOOL CMemPool::AddMemory(DWORD
162. {
163. LPBYTE
164. LPMEMORY_BLOCK newBlock;
165. LPHEADER poolHeader = (LPHEADER)handle;
166. DWORD
167.
168. assert(poolHeader != NULL);
169.
170. // 计算需要分配内存的最小数量,并试图分配之.
171. if (dwSize + sizeof(MEMORY_BLOCK) > MEMPOOL_BLOCK_SIZE)
172. {
173. sizeof(MEMORY_BLOCK);
174. }
175. else
176. {
177. sizeNeeded = MEMPOOL_BLOCK_SIZE;
178. }
179.
180. LPBYTE)HeapAlloc(poolHeader->hHeap, 0, sizeNeeded);
181.
182. if
183. {
184. // 使内存块的头部存储一个MEMORY_BLOCK结构.
185. newBlock = (LPMEMORY_BLOCK)allocedMemory;
186. sizeof(MEMORY_BLOCK);
187. sizeof(MEMORY_BLOCK);
188. newBlock->dwIndex = 0;
189.
190. // 把内存块链接到list中.
191. if(poolHeader->lpHead)
192. {
193. poolHeader->lpHead->lpPrev = newBlock;
194. }
195. newBlock->lpNext = poolHeader->lpHead;
196. newBlock->lpPrev = NULL;
197. poolHeader->lpHead = newBlock;
198. }
199.
200. // 如果allocedMemory 不是 NULL, 则表明我们成功了.
201. return
202. }
CMemPool类使用演示程序代码如下:
1. #include <TCHAR.H>
2. #include "MemPool.h"
3.
4. int
5. {
6. CMemPool mp;
7. assert( mp.Initialize() );
8.
9. for(int
10. {
11. TCHAR* psz = mp.GetStringBuffer(8192);
12. "now i=%d/n"), i);
13. _tprintf(psz);
14. }
15.
16. mp.Destroy();
17. return
18. }
在其中的
- _tprintf(psz);
和
- mp.Destroy();
这两行打上断点,调试运行,观察windows任务管理器关于该进程的内存使用量呈明显的规律性增长。最后当执行完mp.Destroy();后该进程的内存使用量又恢复到未使用内存池时的状态。