原文:ogre内存管理
OGRE内存分配策略相关文件及简述
OGRE提供了自己的内存分配策略,甚至为STL容器提供了新的分配策略,相关文件及简述如下:
OgreMemoryAllocatedObject.h OgreMemoryAllocatedObject.cpp
// 所有使用Ogre内存分配器的类的父类
OgreMemoryAllocatorConfig.h
// 配置内存分配相关规则
OgreMemoryNedAlloc.h OgreMemoryNedAlloc.cpp
// 使用nedalloc库,定义了类NedAllocPolicy、NedAlignedAllocPolicy
OgreMemoryNedPooling.h OgreMemoryNedPooling.cpp
// 使用nedalloc库,定义了类NedPoolingPolicy、NedPoolingAlignedPolicy
OgreMemoryStdAlloc.h
// 定义了类StdAllocPolicy、StdAlignedAllocPolicy
// 只是对malloc/free的简单包装
OgreMemorySTLAllocator.h
// 为STL容器提供的分配器
OgreMemoryTracker.h OgreMemoryTracker.cpp
// 用于跟踪内存的分配和释放,并统计内存的使用和泄漏情况
// *为避免循环引用,OgreMemoryTracker.h头文件必须在OgrePrerequisites.h引用后引用*
OgreAlignedAllocator.h OgreAlignedAllocator.cpp
// 提供对齐内存分配函数
对于OGRE中STL容器的内存分配策略的简述:
以下是摘自OgrePrerequisites.h的代码:
template <typename T, typename A = STLAllocator<T, GeneralAllocPolicy> >
struct vector
{
#if OGRE_CONTAINERS_USE_CUSTOM_MEMORY_ALLOCATOR
typedef typename std::vector<T, A> type;
#else
typedef typename std::vector<T> type;
#endif
};
如上所示,OGRE对STL容器进行了简单的包装,使其默认使用OGRE提供了内存分配策略GeneralAllocPolicy。在
OgreMemoryAllocatorConfig.h头文件中,能够容易找到GeneralAllocPolicy的定义。这样,在使用STL容器时须加“::type”,如Ogre::vector<T>::type,或者照旧使用STL本身的内存分配策略,如std::vector<T>。如需要改变STL容器使用的内存策略,则可以按如下方式使用,Ogre::vector<T,STLAllocator<T,CustomPolicy>>::type,其中CustomPolicy为用户指定的内存策略。
OGRE的内存策略配置及如何自定义内存策略 OGRE的内存策略配置 OGRE中默认所有类型使用一致的内存分配策略,并在编译时便决定。在OgreMemoryAllocatorConfig.h文件中由OGRE_MEMORY_ALLOCATOR宏选择相应的内存策略。 OGRE提供了如下三种内存策略: NedAllocPolicy(NedAlignedAllocPolicy) NedPoolingPolicy(NedPoolingAlignedPolicy) StdAllocPolicy(StdAlignedAllocPolicy) 在OgreConfig.h中定义有 #define OGRE_MEMORY_ALLOCATOR_STD 1 #define OGRE_MEMORY_ALLOCATOR_NED 2 #define OGRE_MEMORY_ALLOCATOR_NEDPOOLING 4 在OgreBuildSettings.h中定义有 #define OGRE_MEMORY_ALLOCATOR 4 即OGRE默认使用的内存分配策略为NedPoolingPolicy。 *** OgreMemoryAllocatorConfig.h文件中定义MemoryCategory枚举类型,以支持不同目的使用不同的内存分配方式,但需用户自己实现。 如何自定义内存分配策略 (假定类名为CustomAllocPolicy,对齐类为CustomAlignAllocPolicy): 1. 在CustomAllocPolicy和CustomAlignAllocPolicy中至少实现如下三个静态函数: //分配内存 static inline void* allocateBytes(size_t count, const char* file = 0, int line = 0, const char* func = 0); //释放内存 static inline void deallocateBytes(void* ptr); //获取一次性分配的最大分配数量 static inline size_t getMaxAllocationSize(); 2. 在OgreConfig.h头文件中添加相应宏定义 如 #define OGRE_MEMORY_ALLOCATOR_CUSTOM 5 3. 在OgreMemoryAllocatorConfig.h头文件中添加代码行,如 #elif OGRE_MEMORY_ALLOCATOR == OGRE_MEMORY_ALLOCATOR_CUSTOM # include "OgreMemoryCustomAlloc.h" //CustomAllocPolicy类所在头文件 namespace Ogre { template <MemoryCategory Cat> class CategorisedAllocPolicy : public CustomAllocPolicy{}; template <MemoryCategory Cat, size_t align = 0> class CategorisedAlignAllocPolicy : public CustomAlignAllocPolicy<align>{}; } 4. 最后在OgreBuildSettings.h中改变定义 #define OGRE_MEMORY_ALLOCATOR 5 |
如何在程序中使用OGRE提供的内存策略
要使用OGRE提供的内存策略(或自定义的内存策略)主要有两种方式:
1. 继承AllocatedObject类
2. 使用OgreMemoryAllocatorConfig.h头文件中定义的内存操作宏
继承AllocatedObject类
AllocatedObject类中重载了new、new[]、delete及delete[]等操作符,所以若要A类使用Ogre提供的或自定义的内存策略,只需继承AllocatedObject类即可。AllocatedObject类以模板的形式支持各种内存分配策略。在OgreMemoryAllocatorConfig.h头文件中对各种模板的AllocatedObject类进行了“重命名”,例如:
typedef CategorisedAllocPolicy<Ogre::MEMCATEGORY_GENERAL> GeneralAllocPolicy;
typedef AllocatedObject<GeneralAllocPolicy> GeneralAllocatedObject;
typedef GeneralAllocatedObject ArchiveAlloc;
typedef GeneralAllocatedObject ConfigAlloc;
所以在继承使用AllocatedObject类时,可考虑使用OgreMemoryAllocatorConfig.h头文件中的宏定义。例如:
(举例多取自OGRE源码,一般都标注了其文件名)
class _OgreExport ConfigFile : public ConfigAlloc //OgreConfigFile.h
class _OgreExport ArchiveManager : public Singleton<ArchiveManager>, public ArchiveAlloc // OgreArchiveManager.h
而为了统一形式,在OgreMemoryAllocatorConfig.h头文件中为继承自AllocatedObject的类定义如下两个宏:
#define OGRE_NEW new
#define OGRE_DELETE delete
因此在使用时,形式如下
ArchiveManager* mArchiveManager = OGRE_NEW ArchiveManager();
使用内存操作宏
除了继承使用AllocatedObject类之外,对于C++中的原始类型(int, double等),或者来自外部库的不能更改的类型,或者某些原因而不能继承AllocatedObject的类型来说,如果要对它们使用Ogre自定义内存策略,则可以使用OgreMemoryAllocatorConfig.h头文件中定义的内存操作宏:
以下三个宏用于分配原始内存,只是分配相应大小的内存,并不进行初始化:
OGRE_MALLOC(bytes, category)
-- 分配bytes大小的内存
OGRE_ALLOC_T(T, count, category)
-- 分配sizeof(T) * count大小的内存
OGRE_FREE(ptr, category)
-- 与上述两个配对使用,释放ptr指向的内存
以下四个宏分配相应内存并进行初始化:
OGRE_NEW_T(T, category)
-- 分配sizeof(T)大小的内存,并调用T的构造函数
OGRE_NEW_ARRAY_T(T, count, category)
-- 分配sizeof(T) * count大小的内存,并对count个T类型实例依次初始化
OGRE_DELETE_T(ptr, T, category)
– 与OGRE_NEW_T 配对使用,调用T的析构函数,释放相应内存
OGRE_DELETE_ARRAY_T(ptr, T, count, category)
--与OGRE_NEW_ARRAY_T配对使用,对count个T类型实例依次调用其析构函数,释放相应内存
举例:
// OgreTexture.cpp
void* pixData = OGRE_MALLOC(dataSize, Ogre::MEMCATEGORY_GENERAL);
// OgreAxisAlignedBox.h
Vector3* mpCorners = OGRE_ALLOC_T(Vector3, 8, MEMCATEGORY_SCENE_CONTROL);
OGRE_FREE(mpCorners, MEMCATEGORY_SCENE_CONTROL);
// OgreAnimationState.cpp
BoneBlendMask* mBlendMask = OGRE_NEW_T(BoneBlendMask, MEMCATEGORY_ANIMATION)(blendMaskSizeHint);
OGRE_DELETE_T(mBlendMask, BoneBlendMask, MEMCATEGORY_ANIMATION);
// OgreBspLevel.cpp
Brush * mBrushes = OGRE_NEW_ARRAY_T(BspNode::Brush, mNumBrushes, MEMCATEGORY_GEOMETRY);
OGRE_DELETE_ARRAY_T(mBrushes, Brush, (size_t)mNumBrushes, MEMCATEGORY_GEOMETRY);
对于以上7个内存操作宏分别有与其对应的对齐方式的宏定义,如OGRE_MALLOC_SIMD、OGRE_MALLOC_ALIGN、OGRE_ALLOC_T_SIMD、OGRE_NEW_T_ALIGN、OGRE_DELETE_ARRAY_T_ALIGN 等等。
***使用这些宏时,如果类型T本身继承于AllocatedObject,对程序本身也不会产生什么坏的影响,除了显得多此一举。
Ogre提供的内存分配/释放跟踪器
在Ogre自己提供的NedAllocPolicy(NedAlignedAllocPolicy)、NedPoolingPolicy(NedPoolingAlignedPolicy)、StdAllocPolicy(StdAlignedAllocPolicy)三种内存分配策略中,都包含了分配/释放跟踪功能,如需在自定义的分配策略中添加跟踪功能,照搬即可。该功能由MemoryTracker类实现,它统计内存的分配和释放情况,以及内存分配语句所在文件名、行号和函数名。默认在程序结束时将统计信息输出到终端,并保存于OgreLeaks.log文件,也可通过成员函数setReportToStdOut(bool rep)和setReportFileName(const std::string& name)设置输出方式。
跟踪器的使用方式都在分配策略中完成,无需在它处额外操作。唯一需要设置的就是跟踪器开关的打开与关闭
在OgrePrerequisites.h文件中有如下宏定义
#if OGRE_DEBUG_MODE
#if OGRE_MEMORY_TRACKER_DEBUG_MODE
#define OGRE_MEMORY_TRACKER 1
#else
#define OGRE_MEMORY_TRACKER 0
#endif
#else
#if OGRE_MEMORY_TRACKER_RELEASE_MODE
#define OGRE_MEMORY_TRACKER 1
#else
#define OGRE_MEMORY_TRACKER 0
#endif
#endif
即,OGRE_MEMORY_TRACKER宏开关取决于OGRE_MEMORY_TRACKER_DEBUG_MODE 或OGRE_MEMORY_TRACKER_RELEASE_MODE。而在OgreBuildSettings.h中定义有
#define OGRE_MEMORY_TRACKER_DEBUG_MODE 0
#define OGRE_MEMORY_TRACKER_RELEASE_MODE 0
即默认是关闭跟踪器的,如果需要在DEBUG版下打开跟踪器,只需要将OGRE_MEMORY_TRACKER_DEBUG_MODE设置为1。而根据OgreMemoryAllocatorConfig.h文件中内存操作宏的设置():
#if OGRE_DEBUG_MODE //语句1
…//详见源文件
#else // !OGRE_DEBUG_MODE
…
#endif // OGRE_DEBUG_MODE
即便是打开了OGRE_MEMORY_TRACKER_RELEASE_MODE宏,跟踪器也不能记录内存分配语句所在文件名、行号和函数名,即不能正常工作。如果需要在Release版下正常开启跟踪器功能,建议将语句1“#if OGRE_DEBUG_MODE”改为“#if OGRE_MEMORY_TRACKER”。
举例:
对于如下代码
#include <string>
#include <OgreRoot.h>
#include <OgreMemoryAllocatorConfig.h>
#include <OgreArchiveManager.h>
using std::string;
int main()
{
{
int *pi = OGRE_NEW_T(int, Ogre::MEMCATEGORY_GENERAL)(50);
OGRE_FREE(pi, Ogre::MEMCATEGORY_GENERAL); //释放
double *pd = OGRE_NEW_ARRAY_T(double, 5, Ogre::MEMCATEGORY_GENERAL);
OGRE_FREE(pd, Ogre::MEMCATEGORY_GENERAL); //释放
string *ps = OGRE_NEW_T(string, Ogre::MEMCATEGORY_GENERAL)("hello");
OGRE_DELETE_T(ps, string, Ogre::MEMCATEGORY_GENERAL); //释放
}
{
Ogre::ArchiveManager* mArchiveManager = OGRE_NEW Ogre::ArchiveManager();
OGRE_DELETE mArchiveManager; //释放
}
return 0;
}
现开启跟踪器宏开关,即将OGRE_MEMORY_TRACKER_DEBUG_MODE 和 OGRE_MEMORY_TRACKER_RELEASE_MODE 都置为1(须重新编译)。该代码正确释放了所有内存,不存在内存泄漏。如果将代码中释放内存的语句全部注释掉,则在Debug版下生成的OgreLeaks.log文件的内容为:
Ogre Memory: Detected memory leaks !!!
Ogre Memory: (6) Allocation(s) with total 244 bytes.
Ogre Memory: Dumping allocations ->
e:\vs project\ogretest\testmain.cpp(12) : {4 bytes} function: main
e:\vs project\ogretest\testmain.cpp(15) : {40 bytes} function: main
e:\vs project\ogretest\testmain.cpp(25) : {64 bytes} function: main
(unknown source):(0) : {52 bytes} function:
(unknown source):(0) : {52 bytes} function:
e:\vs project\ogretest\testmain.cpp(18) : {32 bytes} function: main
在Release版下生成的OgreLeaks.log文件的内容为:
Ogre Memory: Detected memory leaks !!!
Ogre Memory: (8) Allocation(s) with total 248 bytes.
Ogre Memory: Dumping allocations ->
(unknown source):(0) : {48 bytes} function:
(unknown source):(0) : {28 bytes} function:
(unknown source):(0) : {4 bytes} function:
(unknown source):(0) : {4 bytes} function:
(unknown source):(0) : {4 bytes} function:
(unknown source):(0) : {72 bytes} function:
(unknown source):(0) : {40 bytes} function:
(unknown source):(0) : {48 bytes} function:
可以看出在Debug版下,跟踪器正常工作,顺利的检测出全部四处内存泄漏,而在Release版下则不能正常工作。将“#if OGRE_DEBUG_MODE”改为“#if OGRE_MEMORY_TRACKER”后,重新在Release版下测试,跟踪器便能正常工作,生成的OgreLeaks.log的内容为:
Ogre Memory: Detected memory leaks !!!
Ogre Memory: (8) Allocation(s) with total 248 bytes.
Ogre Memory: Dumping allocations ->
(unknown source):(0) : {48 bytes} function:
.\TestMain.cpp(18) : {28 bytes} function: main
.\TestMain.cpp(12) : {4 bytes} function: main
(unknown source):(0) : {4 bytes} function:
(unknown source):(0) : {4 bytes} function:
.\TestMain.cpp(25) : {72 bytes} function: main
.\TestMain.cpp(15) : {40 bytes} function: main
(unknown source):(0) : {48 bytes} function:
P.S.
1. 测试代码中,对int和double类型使用的是OGRE_NEW_T 和 OGRE_FREE 来分别分配和释放内存,而并非前文所说的OGRE_DELETE_T,原因是因为对于int,double等基本类型来说,是没有析构函数的,而OGRE_DELETE_T宏是要调用相应析构函数的。
2. 测试代码中,若将using std::string;注释掉,并将相应string类型测试语句换为如下两句:
std::string *ps2 = OGRE_NEW_T(std::string, Ogre::MEMCATEGORY_GENERAL)("world");
OGRE_DELETE_T(ps2, basic_string, Ogre::MEMCATEGORY_GENERAL);
其中basic_string 不能是string 或 std::string !!! 同样是因为OGRE_DELETE_T宏要调用basic_string类的析构函数。类似情况还有在使用std::fostream,std::fistream等等时。