cocos2dx 学习(-)内存管理机制

前端之家收集整理的这篇文章主要介绍了cocos2dx 学习(-)内存管理机制前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

转自:http://www.jb51.cc/article/p-nnyokbze-baa.html

一、题记

关于cocos2dx 的内存管理机制,想必大家都能清楚说出是通过引用计数(Reference Count)和自动释放池(AutoReleasePool)。但是不知大家是否知道其中具体的运行的细节呢?反正在写这篇blog之前我是一知半解的,而且在粗略的看了下PoolManager 的源码时我还开始怀疑过这个机制的可靠性,于是我还专门找了测内存泄漏的工具vs2010使用vld检测内存泄露,一测发现项目还真存在内存泄漏的问题。可是后来发现原来是自己用的别人的敏感字过滤开源库(https://github.com/xjdrew/crab)存在问题。经我改造后(https://github.com/xzben/GameFrame/tree/master/client/GameClient/frameworks/runtime-src/Classes/crab) 的版本修复此问题,并调整了api的结果。可是修复了我自己的代码的内存泄漏的问题后,发现 Cocos2dx 真的有内存泄漏(哈哈终于抓到尾巴了内心窃喜)。可是发现原来只是cocos2dx 一个单例模式的对象忘记了释放而已(这里我想吐槽cocos2dx 单例模式的实现,这里可以参考我之前写的一篇关于单例模式实现方式的博文http://www.jb51.cc/article/p-fnmodpjc-baa.html),并不是我想得PoolManager 导致的。终于发现原来cocos2dx 的内存管理模式其实应该 是 引用计数(Reference Count)和自动释放池(AutoReleasePool)和 内置CCVector和其它Cocos2dx内置容器类辅助实现的。下面来自己分析一下。


1、首先要说说我为什么怀疑它的可靠性。

因为我发现cocos2dx 的AutoReleasePool 其实是通过PoolManager 在每一帧调用一次clear

  1. voidDisplayLinkDirector::mainLoop()
  2. {
  3. if(_purgeDirectorInNextLoop)
  4. _purgeDirectorInNextLoop=false;
  5. purgeDirector();
  6. }
  7. elseif(!_invalid)
  8. {
  9. drawScene();
  10. //releasetheobjects
  11. PoolManager::getInstance()->getCurrentPool()->clear();
  12. }
  13. }

voidAutoreleasePool::clear()

  • #ifdefined(COCOS2D_DEBUG)&&(COCOS2D_DEBUG>0)
  • _isClearing=true;
  • #endif
  • for(constauto&obj:_managedObjectArray)
  • obj->release();
  • _managedObjectArray.clear();
  • _isClearing=false;
  • }
  • 仔细看了一下clear的实现后发现一个漏洞,那就是 它在将_managedObjectArray 中的obj relase 之后就将_managedObjectArray clear 了,那那些 release之后 引用计数还是大于0的谁来管理呢?我查看了 Node,Ref 的实现中,都没有找到对这些obj 管理,原先使用过程中我们知道 addChild 后就不怕被 PoolManager 给释放了。但是我在addChild中也没看到 retain 在 removeFromParent 也没看到 release 的操作,于是我就产生了疑问。

    2、如何快速找到问题的关键的。

    还好我在上一家公司学会了vs的各种调试技巧(用数据断点的方式查看 Ref 的引用计数变量的变化),让我一下子就找到了问题的所在。原来这个管理工作隐藏在了 内置容器 Vector 中了。

    voidpushBack(Tobject)

  • CCASSERT(object!=nullptr,"Theobjectshouldnotbenullptr");
  • _data.push_back(object);
  • object->retain();
  • voidpopBack()
  • CCASSERT(!_data.empty(),"noobjectsadded");
  • autolast=_data.back();
  • _data.pop_back();
  • last->release();
  • }
  • 3、隐藏的一个危险,不知道大家有没有注意到 Ref 里面一个Debug才会运行的代码

    voidRef::release()
  • CCASSERT(_referenceCount>0,"referencecountshouldgreaterthan0");
  • --_referenceCount;
  • if(_referenceCount==0)
  • #ifdefined(COCOS2D_DEBUG)&&(COCOS2D_DEBUG>0)
  • autopoolManager=PoolManager::getInstance();
  • if(!poolManager->getCurrentPool()->isClearing()&&poolManager->isObjectInPools(this))
  • //Triggeranassertifthereferencecountis0buttheRefisstillinautoreleasepool.
  • //Thishappenswhen'autorelease/release'werenotusedinpairswith'new/retain'.
  • //
  • //Wrongusage(1):
  • //autoobj=Node::create();//Ref=1,butit'sanautoreleaseRefwhichmeansitwasintheautoreleasepool.
  • //obj->autorelease();//Wrong:Ifyouwishtoinvokeautoreleaseseveraltimes,youshouldretain`obj`first.
  • //
  • //Wrongusage(2):
  • //autoobj=Node::create();
  • //obj->release();//Wrong:objisanautoreleaseRef,itwillbereleasedwhenclearingcurrentpool.
  • //Correctusage(1):
  • //autoobj=Node::create();
  • //|-newNode();//`new`isthepairofthe`autorelease`ofnextline
  • //|-autorelease();//Thepairof`newNode`.
  • //obj->retain();
  • //obj->autorelease();//This`autorelease`isthepairof`retain`ofprevIoUsline.
  • //Correctusage(2):
  • //obj->release();//This`release`isthepairof`retain`ofprevIoUsline.
  • CCASSERT(false,"Thereferenceshouldn'tbe0becauseitisstillinautoreleasepool.");
  • #ifCC_USE_MEM_LEAK_DETECTION
  • untrackRef(this);
  • deletethis;
  • }
  • 他在delete之前先判断了下这个obj是否还在管理池中。细想一下这里真的存在一个危险的问题,那就是如果你把一个obj添加到池中了。然后你池中保存了这个obj的指针,但是如果你在添加到池的这一帧中又release了1次这个obj,那么会导致这个内存池中存了一个野指针,那么接下来等着你的就是程序挂掉了。

    4、总结:

    Cocos2dx 中内存管理是基于引用计数(Reference Count)和自动释放池(AutoReleasePool)和 内置CCVector和其它Cocos2dx内置容器类辅助实现的,而且要注意的是它是按帧管理的。也就是它只负责你在前一帧中那些没有被利用到的obj帮你自动处理了。cocos2dx 中 class的 static create 接口都将产生 autorelase 的obj,如果autorelease的obj在产生后没有找到管理的宿主(执行retain),那么就会在下一帧被PoolManager 给clear掉。

    关于cocos2dx 中内存池中这个隐含的危险。我们只要注意不要在同一帧中将一个obj加入池中又将其release就行了。

    另外cocos2dx 中存在大量的单例对象,官方可能存在忘记释放这些单例对象的情况,这时候我们可以通过 vld 快速的定位到,自己进行对应的修改是指能够被正确释放掉。

    猜你在找的Cocos2d-x相关文章