Box2D源码分析:栈内存分配B2StackAllocator
2014-03-11 16:04
218 查看
B2StackAllocator主要是为了运行一个步长时满足box2d需要的临时内存空间,作为栈分配器来防止单步堆分配。(这是官方的说明,我没弄懂是什么意思,有知道的希望能说明一下:-D)
首先给出大概的图给大家一个感受,对于代码更容易理解。
它使用了数据结构stack的后进先出(LIFO)的方式。所以Allocate和Free需要成对使用。
在我看来,它相对于使用SOA实现的B2BlockAllocator主要有以下区别:
a)B2StackAllocator可使用的内存大小固定,而B2BlockAllocator可以扩大内存池;
b)B2StackAllocator内分配的内存大小没有具体要求,只要不超过内存数组剩余大小即可,而B2BlockAllocator只能分配早已指定好的几种block大小类型;
c)B2StackAllocator使用栈思想,后进先出(LIFO),故其分配的空间是连续的。B2BlockAllocator使用链表形式,并利用m_freeLists在这些链表节点中指来指去,故分配的空间可以说是不连续的;
暂时只能想到这些,如有错误或者补充欢迎提出,如果有人能指出两者较为具体的使用环境就更好了。:-D
好了,知道了结构,下面的代码应该问题不大。
首先看一下头文件:
说明已在注释中,下面介绍实现文件。
构造函数
初始化内存池中已使用的内存大小m_index = 0,总分配内存大小(包括堆中)m_allocation = 0,历史总分配内存大小m_maxAllocation = 0,栈实体数量m_entryCount = 0.
析构函数
只需要确保内存池已使用的内存大小以及栈实体数量为0。即验证内存是否完全释放和元素是否完全出栈。故Allocate与free务必成对使用。
内存分配
该部分代码也是简洁易懂,只需注意申请的内存是否超过内存池剩余可用内存。
释放函数
值得注意的只有设置p为NULL吧,其他部分在代码注释中已经说明。
获取历史最大内存分配量
首先给出大概的图给大家一个感受,对于代码更容易理解。
它使用了数据结构stack的后进先出(LIFO)的方式。所以Allocate和Free需要成对使用。
在我看来,它相对于使用SOA实现的B2BlockAllocator主要有以下区别:
a)B2StackAllocator可使用的内存大小固定,而B2BlockAllocator可以扩大内存池;
b)B2StackAllocator内分配的内存大小没有具体要求,只要不超过内存数组剩余大小即可,而B2BlockAllocator只能分配早已指定好的几种block大小类型;
c)B2StackAllocator使用栈思想,后进先出(LIFO),故其分配的空间是连续的。B2BlockAllocator使用链表形式,并利用m_freeLists在这些链表节点中指来指去,故分配的空间可以说是不连续的;
暂时只能想到这些,如有错误或者补充欢迎提出,如果有人能指出两者较为具体的使用环境就更好了。:-D
好了,知道了结构,下面的代码应该问题不大。
首先看一下头文件:
const int32 b2_stackSize = 100 * 1024; //内存数组最大内存100k const int32 b2_maxStackEntries = 32; //能存储的最多的栈实体数量 struct b2StackEntry { char* data; //指向使用的内存头位置 int32 size; //该内存大小 bool usedMalloc; //是否在堆上申请 }; // This is a stack allocator used for fast per step allocations. // You must nest allocate/free pairs. The code will assert // if you try to interleave multiple allocate/free pairs. class b2StackAllocator { public: b2StackAllocator(); ~b2StackAllocator(); void* Allocate(int32 size); void Free(void* p); int32 GetMaxAllocation() const; //获取历史总分配内存大小(不扣释放掉的) private: //内存数组,看成内存池呗 char m_data[b2_stackSize]; //内存池中已使用的内存大小 //可用来获取内存池未使用的内存头位置 int32 m_index; //栈实体数组m_entries中所有实体的内存总大小 //与上面m_index不同的是 //m_allocation包括了在堆上分配的内存大小 int32 m_allocation; //历史上总分配内存大小 int32 m_maxAllocation; //栈实体数组 b2StackEntry m_entries[b2_maxStackEntries]; //栈实体数组中实体数量 int32 m_entryCount; };
说明已在注释中,下面介绍实现文件。
构造函数
b2StackAllocator::b2StackAllocator() { m_index = 0; m_allocation = 0; m_maxAllocation = 0; m_entryCount = 0; }
初始化内存池中已使用的内存大小m_index = 0,总分配内存大小(包括堆中)m_allocation = 0,历史总分配内存大小m_maxAllocation = 0,栈实体数量m_entryCount = 0.
析构函数
b2StackAllocator::~b2StackAllocator() { b2Assert(m_index == 0); b2Assert(m_entryCount == 0); }
只需要确保内存池已使用的内存大小以及栈实体数量为0。即验证内存是否完全释放和元素是否完全出栈。故Allocate与free务必成对使用。
内存分配
void* b2StackAllocator::Allocate(int32 size) { //确保栈实体数组还未使用完毕 b2Assert(m_entryCount < b2_maxStackEntries); //获取实体数组中未使用的头位置 //不过,既然是数组 //为什么不直接使用b2StackEntry entry = m_entries[m_entryCount]呢? b2StackEntry* entry = m_entries + m_entryCount; entry->size = size; if (m_index + size > b2_stackSize) { //若申请的大小超过剩余可分配数 //则在堆上申请内存 //同时设usedMalloc = true entry->data = (char*)b2Alloc(size); entry->usedMalloc = true; } else { //若申请的大小未超过剩余可分配数 //直接在内存池中申请内存 //获取内存池中未使用的内存首地址 entry->data = m_data + m_index; //设usedMalloc = false entry->usedMalloc = false; //增加内存池中内存使用量 m_index += size; } //增加内存使用量(包括堆中) m_allocation += size; //设置历史最大分配内存 m_maxAllocation = b2Max(m_maxAllocation, m_allocation); //增加栈实体使用数 ++m_entryCount; return entry->data; }
该部分代码也是简洁易懂,只需注意申请的内存是否超过内存池剩余可用内存。
释放函数
void b2StackAllocator::Free(void* p) { //要释放自然要有可以释放的元素啦 //确保有元素可以释放 b2Assert(m_entryCount > 0); //获取最后一个实体,即准备要POP栈顶部啦 b2StackEntry* entry = m_entries + m_entryCount - 1; //保证释放的内存确实是参数内存 //这里再次提醒,一个Allocate务必对应一个Free //否则会造成这里assert b2Assert(p == entry->data); if (entry->usedMalloc) { //在堆中申请的 //释放堆中内存 b2Free(p); } else { //在内存池中申请的 //减少已使用的内存 //相当于设置其为可以被使用 //下次有新申请直接把它送出去,任由其被覆盖 m_index -= entry->size; } //减少总分配内存量 m_allocation -= entry->size; //减少实体数组中已使用的实体量 --m_entryCount; //注意设置p为null p = NULL; }
值得注意的只有设置p为NULL吧,其他部分在代码注释中已经说明。
获取历史最大内存分配量
int32 b2StackAllocator::GetMaxAllocation() const { //直接返回成员变量m_maxAllocation即可 return m_maxAllocation; }
相关文章推荐
- Java对象内存分配原理及源码分析
- nginx 源码学习笔记(七)——内存分配相关源码分析
- Libevent源码分析-----内存分配
- 源码分析:Java对象的内存分配
- 内存、分配-OMXCodec源码分析---part2-by小雨
- 源码分析:Java对象的内存分配
- 【golang 源码分析】内存分配与管理
- 深入分析Linux内核源码——6.3 内存的分配和回收
- Redis源码分析-内存分配
- python源码分析----内存分配(2)
- Libevent源码分析-----内存分配
- MySQL源码分析(2):Mysql中的内存分配相关
- CloudSim源码分析之主机资源分配(内存篇)
- Nginx源码分析(1)之——共享内存的配置、分配及初始化
- Libevent源码分析-----内存分配
- python源码分析----内存分配(1)
- nginx 源码学习笔记(七)——内存分配相关源码分析
- freertos 内存分配 源码分析
- 源码分析:Java对象的内存分配
- 【redis源码分析】内存分配---zmalloc