Ogre 中的 MemoryManager 仅用于 debug,检查是否存在泄漏、越界访问等非法情况,并不是优化用的小块内存分配器。
相关文件:
OgreMemoryManager.h/.cpp MemoryManager 的实现
OgreMemoryMacros.h 用宏替换普通的 new/delete, malloc/free
OgreNoMemoryMacros.h 抵消 OgreMemoryMacros.h 的效果,还原普通的 new/delete, malloc/free(实现 MemoryManager 时需要)
OgrePrerequisites.h 中 #include "OgreMemoryManager.h",因此可以让所有模块都引入 MemoryManager。
启用 MemoryManager 的方法:
#define OGRE_DEBUG_MEMORY_MANAGER 1
=====================
wipeWithPattern(),将 sAllocUnit.actualAddress 所指向的内存全部写为 pattern 的内容。
dumpAllocations(),进程所申请的所有内存块的情况。
MemoryManager::dllocMem() 的时候会检查 new/delete, new []/delete [], malloc/free 是否匹配。
每两次 MemoryManager::allocMem() 之间,检查所有已分配的内存块是否存在越界访问(检查 padding 是被写坏)。
=====================
new --> MemoryManager::op_new_sc() --> MemoryManager::allocMem
new [] --> MemoryManager::op_new_vc() --> MemoryManager::dllocMem
delete --> MemoryManager::op_del_sc() --> MemoryManager::allocMem
delete [] --> MemoryManager::op_del_vc() --> MemoryManager::dllocMem
=====================
几个全局变量,决定了 MemoryManager 的整个运作机制:
struct sMStats;
sAllocUnit *reservoir;
sAllocUnit *hashTable[hashSize];
sAllocUnit **reservoirBuffer;
unsigned int reservoirBufferSize;
下面我们来一一分析之。
=====================
全局变量 struct sMStats; 包括了一些基本的统计信息。
totalReportedMemory - 当前申请的内存总数
totalActualMemory - 当前实际的内存总数
peakReportedMemory - 申请的内存总数的峰值
peakActualMemory - 实际的内存总数的峰值
accumulatedXXXX - 内存使用的积累总量(只增不减)
因此,当所有申请的内存释放后,totalActualMemory == 0,则没有内存泄漏。
=====================
每次 allocMem() 会判断 reservoir 中是否还有空闲 au,没有了则再申请 256 个,并将所申请的 256 个 au 放入 reservoirBuffer。
reservoir, 当前空闲的 sAllocUnit List (free list)
reservoirBuffer, 所有申请的 sAlloocUnit 的集合
reservoirBufferSize, 数组 reservoirBuffer[] 的大小
allocMem() 会根据 reportedSize 分配一个 actualSize 大小的内存,主要目的是产生了前后两块 padding,用以检查是否存在越界访问。
所有已经被征用的 au,会被放到全局变量 hashTable[] 中。程序退出时,如果 totalActualMemory != 0,则 hashTable 中的所有 au 都是泄漏的。
------------------
2008-09-07 补充 从 new/delete 中截取 __FILE__, __LINE__ 信息
今天和 nescafe 讨论 new/delete 无法如 malloc/free 一般,很好的获取 __FILE__, __LINE__ 这些信息。突然想到 OgreMemoryManager 其实很好地实现了此目的,只是我当时阅读代码时,并没有很好地理解这一段代码的用意。
因为语法的关系,我们无法像 malloc/free 一样,简单地弄个宏就搞定。
#define malloc(size) my_malloc(size, __FILE__, __LINE__)
#define free(p) my_free(p, __FILE__, __LINE__)
自然,想重载函数也是不行的。
void *operator new(size_t size, const char* file, const int line);
void operator delete(void *p, const char* file, const int line);
Ogre 则通过一个小小的 trick 做到了,呵呵,如下,我做了一个简化版本:
// ====== my_alloc.h =======
#ifndef MY_ALLOC
#define MY_ALLOC
void make_record(char const *file, int line, char const *func);
void *my_new(size_t size);
void my_delete(void * p);
inline void *operator new(size_t size)
{
return my_new(size);
}
inline void *operator new[](size_t size)
{
return my_new(size);
}
inline void operator delete(void *p)
{
my_delete(p);
}
inline void operator delete[](void *p)
{
my_delete(p);
}
#define new (make_record(__FILE__, __LINE__, __FUNCTION__), false) ? 0 : new
#define delete (make_record(__FILE__, __LINE__, __FUNCTION__), false) ? 0 : delete
#endif
// ====== my_alloc.cpp =======
#include "my_alloc.h"
#include <stdlib.h>
#include <stdio.h>
static char const * cur_file;
static int cur_line;
static char const * cur_func;
void make_record(char const *file, int line, char const *func)
{
cur_file = file;
cur_line = line;
cur_func = func;
}
void *my_new(size_t size)
{
printf("new() file = %s, line = %d, func = %s\n", cur_file, cur_line, cur_func);
return malloc(size);
}
void my_delete(void * p)
{
printf("new() file = %s, line = %d, func = %s\n", cur_file, cur_line, cur_func);
free(p);
}
// ======== main.cpp =========
#include "my_alloc.h"
int main(void)
{
int *p = new int;
delete p;
p = new int[10];
delete [] p;
return 0;
}