Box2d源码学习<三> 内存管理之栈的实现

本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8271251

Box2d上有两个和栈有关的类,它们分别是b2StackAllocator和b2GrowableStack。

B2StackAllocator主要是为了运行一个步长时满足box2d需要的临时内存空间,作为栈分配器来防止单步堆分配。

B2GrowableStack主要是为了满足动态树b2DynaicTree中光线投射和区域查询所需要的临时内存空间,这个到动态树的时候再做详细研究。


下面我们就来看这两个类是如何实现的。

1、 b2StackAllocator类


首先我们看它头文件b2StackAllocator.h

看下相关常量和结构体的定义:

[cpp]  view plain copy
  1. //栈中内存池的大小  
  2. const int32 b2_stackSize = 100 * 1024;  // 100k  
  3. //栈元素的最大数量  
  4. const int32 b2_maxStackEntries = 32;  
  5. //栈实体定义  
  6. struct b2StackEntry  
  7. {  
  8.     char* data;          //头指针  
  9.     int32 size;          //大小  
  10.     bool usedMalloc;     //是否使用  
  11. };  

代码上面有注释,不多说了。

下面看看b2StackAllocator类的定义吧(好期待(⊙o⊙)哦)

[cpp]  view plain copy
  1. // 这是一个栈分配器用于每一步都快速的分配  
  2. // 你必须成对使用allocate/free这对函数。  
  3. // 如果你使用allocate/free次数不同时,将会出现断言。  
  4. class b2StackAllocator  
  5. {  
  6. public:  
  7.       
  8.     /************************************************************************** 
  9.     * 功能描述:构造函数 
  10.     * 参数说明:(void) 
  11.     * 返 回 值:无 
  12.     **************************************************************************/  
  13.     b2StackAllocator();  
  14.     /************************************************************************** 
  15.     * 功能描述:析构函数 
  16.     * 参数说明:(void) 
  17.     * 返 回 值:无 
  18.     **************************************************************************/  
  19.     ~b2StackAllocator();  
  20.     /************************************************************************** 
  21.     * 功能描述:申请内存函数 
  22.     * 参数说明:size :需要申请的内存大小 
  23.     * 返 回 值:申请的内存头指针 
  24.     **************************************************************************/  
  25.     void* Allocate(int32 size);  
  26.     /************************************************************************** 
  27.     * 功能描述:释放内存函数 
  28.     * 参数说明:p    :释放内存的头指针 
  29.     * 返 回 值:(void) 
  30.     **************************************************************************/  
  31.     void Free(void* p);  
  32.     /************************************************************************** 
  33.     * 功能描述:获取已申请的内存容量的最大值 
  34.     * 参数说明:(void) 
  35.     * 返 回 值:曾经进栈过所有元素【不管现在是否出栈】使用的内存容量 
  36.     **************************************************************************/  
  37.     int32 GetMaxAllocation() const;  
  38.   
  39. private:  
  40.     //栈的内存池,用于栈子节点的内存开辟  
  41.     char m_data[b2_stackSize];  
  42.     //在栈的内存池中,已使用的内存大小  
  43.     int32 m_index;  
  44.     //栈中的所有元素使用内存大小  
  45.     int32 m_allocation;  
  46.     //曾经进栈过所有元素【不管现在是否出栈】使用的内存容量  
  47.     //注意该变量在对象销毁之前只增不减  
  48.     int32 m_maxAllocation;  
  49.     //栈实体的数组  
  50.     b2StackEntry m_entries[b2_maxStackEntries];  
  51.     //栈中元素的数量  
  52.     int32 m_entryCount;  
  53. };  

同样不多说了,看注释。我们再来看看b2StakAllocator是如何实现的:

[cpp]  view plain copy
  1. b2StackAllocator::b2StackAllocator()  
  2. {  
  3.     //初始化相关变量  
  4.     m_index = 0;  
  5.     m_allocation = 0;  
  6.     m_maxAllocation = 0;  
  7.     m_entryCount = 0;  
  8. }  
  9. b2StackAllocator::~b2StackAllocator()  
  10. {  
  11.     //验证内存是否已完全释放  
  12.     b2Assert(m_index == 0);  
  13.     //验证元素是否已完全出栈  
  14.     b2Assert(m_entryCount == 0);  
  15. }  

下面是Allocate、Free、GetMaxAllocation函数,看代码:

[cpp]  view plain copy
  1. void* b2StackAllocator::Allocate(int32 size)  
  2. {  
  3.     //验证栈中元素的有效性,防止内存溢出  
  4.     b2Assert(m_entryCount < b2_maxStackEntries);  
  5.     //获取栈实体头指针  
  6.     b2StackEntry* entry = m_entries + m_entryCount;  
  7.     //实体大小  
  8.     entry->size = size;  
  9.     //当内存池m_data已使用的大小加需要申请的大小大于内存池的总容量时,则在堆上申请  
  10.     if (m_index + size > b2_stackSize)  
  11.     {  
  12.         //申请大小为size的内存,并标记是在堆上申请的  
  13.         entry->data = (char*)b2Alloc(size);  
  14.         entry->usedMalloc = true;  
  15.     }  
  16.     else  
  17.     {  
  18.         //从m_data中获取内存,并标记不是在堆上申请的  
  19.         //同时修改m_index的值  
  20.         entry->data = m_data + m_index;  
  21.         entry->usedMalloc = false;  
  22.         m_index += size;  
  23.     }  
  24.     //增加栈中的所有元素使用内存大小  
  25.     m_allocation += size;  
  26.     //修改内存容量的最大值  
  27.     m_maxAllocation = b2Max(m_maxAllocation, m_allocation);  
  28.     //增加栈中元素的数量  
  29.     ++m_entryCount;  
  30.     //返回栈中元素的内存头指针  
  31.     return entry->data;  
  32. }  
  33.   
  34. void b2StackAllocator::Free(void* p)  
  35. {  
  36.     //验证栈中元素的有效性  
  37.     b2Assert(m_entryCount > 0);  
  38.     //栈中数量减1,在这里用数组模拟了出栈  
  39.     b2StackEntry* entry = m_entries + m_entryCount - 1;  
  40.     b2Assert(p == entry->data);  
  41.     //是否是在堆上申请的  
  42.     if (entry->usedMalloc)  
  43.     {  
  44.         //释放p  
  45.         b2Free(p);  
  46.     }  
  47.     else  
  48.     {  
  49.         //将索引值减去栈实体的内存大小  
  50.         m_index -= entry->size;  
  51.     }  
  52.    //减去已释放的内存大小  
  53.     m_allocation -= entry->size;  
  54.     //元素数量减少  
  55.     --m_entryCount;  
  56.     //将指针置空,防止野指针  
  57.     p = NULL;  
  58. }  
  59. int32 b2StackAllocator::GetMaxAllocation() const  
  60. {  
  61.     return m_maxAllocation;  
  62. }  

对于Allocate函数,我们用原先分配了大小b2_stackSize的内存池辅助数组。同时也用m_index记录了已使用的内存池的大小。若内存池中剩余内存不够则在堆上申请,否则在内存池中获取相应大小的内存。

同样Free函数也有两种情况,在堆上申请的内存直接释放,在内存池中申请的内存返还到内存池中,不用释放。

GetMaxAllocation函数在同一个对象的生命周期里是返回的值是只增不减的。

 

2、 b2GrowableStack类

本类定义、实现均在b2GrowableStack.h文件中

[cpp]  view plain copy
  1. //这是一个可增长的先进后出初始容量为N的栈  
  2. //如果栈的大小达到初始容量,则在堆中增加栈的大小  
  3. template <typename T, int32 N>  
  4. class b2GrowableStack  
  5. {  
  6. public:  
  7.     /************************************************************************** 
  8.     * 功能描述:栈构造函数,初始化相关数据 
  9.     * 参数说明:(void) 
  10.     * 返 回 值:(void) 
  11.     **************************************************************************/  
  12.     b2GrowableStack()  
  13.     {  
  14.         m_stack = m_array;  
  15.         m_count = 0;  
  16.         m_capacity = N;  
  17.     }  
  18.     /************************************************************************** 
  19.     * 功能描述:栈析构函数,释放相关内存 
  20.     * 参数说明:(void) 
  21.     * 返 回 值:(void) 
  22.     **************************************************************************/  
  23.     ~b2GrowableStack()  
  24.     {  
  25.         if (m_stack != m_array)  
  26.         {  
  27.             b2Free(m_stack);  
  28.             m_stack = NULL;  
  29.         }  
  30.     }  
  31.       
  32.     /************************************************************************** 
  33.     * 功能描述:进栈操作 
  34.     * 参数说明:element :进栈元素 
  35.     * 返 回 值:(void) 
  36.     **************************************************************************/  
  37.     void Push(const T& element)  
  38.     {  
  39.         //栈已满  
  40.         if (m_count == m_capacity)  
  41.         {  
  42.             //获取栈头指针并保存到old中  
  43.             T* old = m_stack;  
  44.             //将栈的容量扩充到原来的2倍  
  45.             m_capacity *= 2;  
  46.             //申请内存,并保存到m_stack中  
  47.             m_stack = (T*)b2Alloc(m_capacity * sizeof(T));  
  48.             //将原来的内容整体拷贝到m_stack中去  
  49.             std::memcpy(m_stack, old, m_count * sizeof(T));  
  50.             if (old != m_array)  
  51.             {  
  52.                 //释放old指向的内存  
  53.                 b2Free(old);  
  54.             }  
  55.         }  
  56.         //进栈,并将元素个数自加  
  57.         m_stack[m_count] = element;  
  58.         ++m_count;  
  59.     }  
  60.       
  61.     /************************************************************************** 
  62.     * 功能描述:出栈操作 
  63.     * 参数说明:(void) 
  64.     * 返 回 值:返回最近进入栈的值 
  65.     **************************************************************************/  
  66.     T Pop()  
  67.     {  
  68.         //验证元素个数的有效性  
  69.         b2Assert(m_count > 0);  
  70.         //元素个数自减,并出栈  
  71.         --m_count;  
  72.         return m_stack[m_count];  
  73.     }  
  74.     /************************************************************************** 
  75.     * 功能描述:获取栈中元素的个数 
  76.     * 参数说明:(void) 
  77.     * 返 回 值:栈中元素的个数 
  78.     **************************************************************************/  
  79.     int32 GetCount()  
  80.     {  
  81.         return m_count;  
  82.     }  
  83.   
  84. private:  
  85.     //栈头指针  
  86.     T* m_stack;  
  87.     //辅助数组  
  88.     T m_array[N];  
  89.     //元素的个量  
  90.     int32 m_count;  
  91.     //栈的容量  
  92.     int32 m_capacity;  
  93. };  

该类是个模板类,所有元素的类型和栈的大小均有使用的时候自己定义,同样是由内存池即辅助数组模拟的入栈和出栈。不同地方是当内存池不足时,b2GrowableStack会重新申请一个是原来一倍容量更大的新的内存池,并拷贝旧内存池中的内容到新的内存池中,同时释放除辅助数组以外的旧的内存池,不过多次扩充内存对将会有一定的影响,最好能够使用之前先估算一下所需内存池的大小。对于push/pop函数,均用数组模拟的进出栈。其他也没啥好说的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值