Cocos2d-x内存管理

Cocos2d-x内存管理浅解

1、首先我们知道内存管理分为c++自身管理机制以及Cocos2d-x内存管理机制。在c++中。内存分为堆区、栈区、静态存储区(全局存储区)、常量存储区、自由存储区。

      主要先说一下堆区和栈区。

堆区主要由new和malloc分配。new与delete,malloc与free成对出现,保证内存的分配与回收。堆内存分配地址是逐渐增大的,这一点与栈区相反,我们都知道栈是先进后出。所以栈的存储方向是内存地址逐渐减小的。栈中的内存也是系统自己主动回收的,这个我们不需要考虑自己管理内存泄露的问题,而堆区的释放一定要注意使用完后手动释放,不然就会出现内存泄露的问题。在小的程序中我们可能感受不到这个问题的严重性,在成品软件中我们必需要严防这个问题,非常多时候在PC端开发的时候没有感觉到不论什么问题。可是版本号公布后就会发现出现非常多Crash。这个当中非常重要的原因就是由于内存泄露。我们常常说手机内存的大小。当手机装了一大堆软件之后就会变得非常卡,就是这个原因,内存泄露之后正在执行的程序越来越感觉内存不够用,严重的就是不断卡顿然后Crash。所以要养成习惯,注意内存使用与回收的问题。

      另外关于静态存储区的问题,我们须要注意几点,静态函数与静态成员变量是类所拥有的不是某个对象所独有的,静态成员能够由类名调用,统一赋值或改变,这是静态成员的长处相同也是缺点,当我们须要一个全局变量时我们通常会使用静态变量。使用十分方便可是当不同的对象须要对其改变而其它对象不须要时则会发生数据不稳定的情况,这是我们不愿意看到的。

对于其长处我们一般在须要全局出现的地方使用。比如单例模式中,我们须要一个单例类,单例类对象的初始化函数就是静态成员,表明全局性。而我们知道静态函数仅仅能操作静态数据,所以我们的成员指针也必须是静态的。这样我们就实现了对象的唯一性。
2、cocos2d-x内存管理机制

     cocos2d-x内存管理总的原则是谁拥有 。 谁管理 , 谁释放。

      cocos2d-x使用引用计数对内存进行管理。当我们在堆上分配一块内存空间的时候,这个对象的引用计数就是1,当有对象要引用这块内存空间的时候,这个引用计数就添加1。当有对象不再引用这块内存的时候引用计数就减1,当这个引用计数减为0的时候就使用delete删除掉这块内存,这样就做到了当有对象引用的时候会正常的訪问这块内存。引用完成也能够正常的回收。可是假设每个对象所有是手动retain和release,那么我们的代码里就会到处都是retain、release,既不美观方便也easy出错,于是我们引入了自己主动内存管理池对对象进行管理。看例如以下代码。引用计数问题就会比較清楚了。

          //对象创建的时候引用计数被设置为1。这个是在它的构造函数中完毕的,它会先调用父类CCOjbect的构造函数
          //CCObject的构造函数例如以下所看到的
          //CCObject::CCObject(void)
          //, m_uReference(1) {}// when the object is created, the reference count of it is 1
    CCSprite * sprite = new CCSprite();
 
    CCLOG("retain count:%d",sprite->retainCount()); //值为1
 
    //调用retain方法的时候引用计数添加1
    sprite->retain();
    CCLog("retain count:%d",sprite->retainCount());
 
    //调用release方法的时候引用计数减一,当这个引用计数减为0的时候,在release方法中会delete掉这个对象
    sprite->release();
    CCLog("retain count:%d",sprite->retainCount());
 
    //当我们调用autorelease方法的时候会调用这段代码
    //CCPoolManager::sharedPoolManager()->addObject(this);
    //调用autorelease方法的时候对象会被放到自己主动回收池中。这个自己主动回收池在每帧结束的时候会调用一次对象的release方法
    sprite->autorelease();
    CCLog("retain count:%d",sprite->retainCount());//记住 autoRelease并不会添加引用计数
    上面代码有一处不太好理解就是常常说的自己主动回收机制,也就是autorelease方法,这种方法究竟做了什么。有什么作用。我们须要好好的搞清楚!首先须要澄清的一个概念就是帧,在Cocos2d-x中我们常常说每秒多少多少帧,事实上这个帧须要多少时间不是固定的,这个须要看每帧我们须要做多少事情,假设每一帧我们须要渲染非常多的东西,那这一帧运行的时间当然就会非常长的,游戏显得就会非常卡,这个时候每秒的帧率就会下降的,所以不是时间决定的帧率,而是帧影响的时间!这个自己主动回收池就是在每帧结束的时候起作用的,在游戏的每一帧都会有一个大的循环。在一帧開始之前。系统建立了一个内存回收池。在这一帧的过程中,当我们调用了autorelease方法以后。我们的对象就会放到这个内存回收池中,当一帧结束的时候这个内存回收池就会释放掉,这个时候在内存回收池中的对象就会被release一下,也就是说引用计数就会减一,假设这个时候引用计数为0。就会删除对象了。假设引用计数不为0的话对象是不会被删除的,这时改由对象的持有者管理其生存周期了,下一帧開始的时候系统又会创建一个内存回收池,这个时候在上一次加入的对象这个时候是不会又一次加入到这个内存回收池中的,在这个内存回收池中的对象是你在这一帧中调用了autorelease函数的对象。好了以下我们来看一个样例来了解下这个自己主动回收机制怎么就做到自己主动回收的。
<code cpp>
//在create的时候调用了sprite的autorelease方法
CCSprite * sprite = CCSprite::create("HelloWorld.png");
CCLog("retain count:%d",sprite->retainCount());         //retain 1
this->addChild(sprite);
CCLog("retain count:%d",sprite->retainCount());         //retain 2
    分析一下上边的代码。调用了create工厂方法以后,内部的实现是先new一个CCSprite的对象,这个时候引用计数加1。然后调用autorelease方法,将这个对象放到了自己主动回收池中,由于这一帧还没有结束。当然引用计数就还是1,所以打印的结果就是1。当我们调用addChild的时候,传入这个CCSprite对象。这个时候在当前层接受了这个对象以后会把它的引用计数加一。表明当前层正在使用这块内存空间,所以如今的retain就是2了。当这一帧结束的时候自己主动回收池会将对象的引用计数-1,所以如今就仅仅有CCLayer在引用这个对象了。当CCLayer析构的时候。它会调用这个对象的release方法。这个时候当然就会删除了这个CCSprite对象了。所以自己主动回收机制的自己主动就是在这一帧结束的时候将对象開始new的时候加的那个引用计数减掉,而让引擎中持有对象引用的其它对象去管理这个对象。当持有者removeChild()时或者自身析构的时候该对象就会被释放。即对象的持有者负责管理对象的retain和release。从而实现谁持有。谁管理,谁释放的原则。



    *** cocos2d-x内存管理CCObject与autorelease深入分析 
引用计数器是一种内存管理方式。

通过一个无符号的成员变量计算当前有多少使用者在使用本内存。

每次外部对象使用本内存时计数器加1,使用完要释放本内存时计数器减1,当计数器减为0时才真正进行占用内存释放。这样做能够实如今多个使用者使用一块内存时,仅仅有当全部的使用者都确定不再使用这块内存的时候才进行内存的释放。

避免了在还有使用者在使用内存时提前释放内存而导致的程序崩溃。

    有兴趣的同学能够看引擎的源编,在源代码中我们看到CCObject做的工作比較简单,它主要就是有两个功能。

一个是通过引用计数交给内存管理器进行内存管理。还有一个就是通过脚本ID訪问对应的脚本。脚本的分析临时不探讨,我们临时仅仅把内存管理的事情搞明确。

我们来重点看一下autorelease函数的意义。顾名思义。“自己主动释放”。也就是说调用此函数则当前CCObject实例对象不须要用户在外部去手动调用release进行内存的释放工作。

我们上面已经知道它通过引用计数来处理在什么时候内存释放,那么Cocos2d-x是怎么做到的?
    在autorelease函数中有这么一句:CCPoolManager::sharedPoolManager()->addObject(this);
    CCPoolManager代表了内存管理器。此句调用CCPoolManager的实例对象的addObject函数将当前CCObject实例对象的指针交给内存管理器。内存管理器负责管理CCAutoReleasePool,CCAutoReleasePool中增加的就是被自己主动管理的对象。

    以下来看一下内存管理器的原理:
     CCMutableArray是一个CCObject指针容器类。它内部通过使用STL的vector容器来存储CCObject指针。

在增加一个新CCObject时对其引用计数器加1,在移除CCObject时对其引用计数器减1。

有兴趣的同学能够自行打开CCMutableArray.h及cpp文件进行查看。



    那么上面的介绍大家应该已经有了一个比較清楚的了解了, 最后再总结一下:Cocos2d-x提供了一个内存管理器类CCPoolManager,它有一个容器。而这个容器是用来存放了一些容器管理类CCAutoreleasePool的实例对象的。须要自己主动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。全部存在当中的CCObject实例对象在进行释放操作时通过使用计数器来进行推断在何时真正delete内存。

原文地址:https://www.cnblogs.com/cynchanpin/p/7306313.html