本文主要是介绍cocos2dx 学习(-)内存管理机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
转自:http://blog.csdn.net/xzben/article/details/41979601
一、题记
关于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://blog.csdn.net/xzben/article/details/17080757#t14),并不是我想得PoolManager 导致的。终于发现原来cocos2dx 的内存管理模式其实应该 是 引用计数(Reference Count)和自动释放池(AutoReleasePool)和 内置CCVector和其它Cocos2dx内置容器类辅助实现的。下面来自己分析一下。
1、首先要说说我为什么怀疑它的可靠性。
因为我发现cocos2dx 的 AutoReleasePool 其实是通过 PoolManager 在每一帧调用一次clear
- void DisplayLinkDirector::mainLoop()
- {
- if (_purgeDirectorInNextLoop)
- {
- _purgeDirectorInNextLoop = false;
- purgeDirector();
- }
- else if (! _invalid)
- {
- drawScene();
- // release the objects
- PoolManager::getInstance()->getCurrentPool()->clear();
- }
- }
- void AutoreleasePool::clear()
- {
- #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
- _isClearing = true;
- #endif
- for (const auto &obj : _managedObjectArray)
- {
- obj->release();
- }
- _managedObjectArray.clear();
- #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
- _isClearing = false;
- #endif
- }
2、如何快速找到问题的关键的。
还好我在上一家公司学会了vs的各种调试技巧(用数据断点的方式查看 Ref 的引用计数变量的变化),让我一下子就找到了问题的所在。原来这个管理工作隐藏在了 内置容器 Vector 中了。
- void pushBack(T object)
- {
- CCASSERT(object != nullptr, "The object should not be nullptr");
- _data.push_back( object );
- object->retain();
- }
- void popBack()
- {
- CCASSERT(!_data.empty(), "no objects added");
- auto last = _data.back();
- _data.pop_back();
- last->release();
- }
3、隐藏的一个危险,不知道大家有没有注意到 Ref 里面一个Debug才会运行的代码
- void Ref::release()
- {
- CCASSERT(_referenceCount > 0, "reference count should greater than 0");
- --_referenceCount;
- if (_referenceCount == 0)
- {
- #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
- auto poolManager = PoolManager::getInstance();
- if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
- {
- // Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
- // This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
- //
- // Wrong usage (1):
- //
- // auto obj = Node::create(); // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
- // obj->autorelease(); // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
- //
- // Wrong usage (2):
- //
- // auto obj = Node::create();
- // obj->release(); // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
- //
- // Correct usage (1):
- //
- // auto obj = Node::create();
- // |- new Node(); // `new` is the pair of the `autorelease` of next line
- // |- autorelease(); // The pair of `new Node`.
- //
- // obj->retain();
- // obj->autorelease(); // This `autorelease` is the pair of `retain` of previous line.
- //
- // Correct usage (2):
- //
- // auto obj = Node::create();
- // obj->retain();
- // obj->release(); // This `release` is the pair of `retain` of previous line.
- CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
- }
- #endif
- #if CC_USE_MEM_LEAK_DETECTION
- untrackRef(this);
- #endif
- delete this;
- }
- }
4、总结:
Cocos2dx 中内存管理是基于 引用计数(Reference Count)和自动释放池(AutoReleasePool)和 内置CCVector和其它Cocos2dx内置容器类辅助实现的,而且要注意的是它是按帧管理的。也就是它只负责你在前一帧中那些没有被利用到的obj帮你自动处理了。cocos2dx 中 class的 static create 接口都将产生 autorelase 的obj,如果autorelease的obj在产生后没有找到管理的宿主(执行retain),那么就会在下一帧被PoolManager 给clear掉。
关于cocos2dx 中内存池中这个隐含的危险。我们只要注意不要在同一帧中将一个obj加入池中又将其release就行了。
另外cocos2dx 中存在大量的单例对象,官方可能存在忘记释放这些单例对象的情况,这时候我们可以通过 vld 快速的定位到,自己进行对应的修改是指能够被正确释放掉。
这篇关于cocos2dx 学习(-)内存管理机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!