Cocos2d-X中的内存优化

一、Cocos2d-X中的内存问题

游戏中的内存优化一直是个让人头疼的问题,特别是对于C++程序员来说,指针和内存需要主动去创建及释放.
特别是对内存概念不清楚的技术人员,经常会遇到指针丢失导致突然崩溃,内存越来大但是不知道何处占用.解决起来也是一头雾水,本篇文章粗略讲解一下cocos2d-X中的内存管理及优化

1. 分析工具

在windows开发中可以使用vs自带的诊断工具进行内存监测及分析.程序启动后,打开诊断工具的堆分析,可以通过截取快照来记录某个时间点的内存信息


![image.png](https://img-blog.csdnimg.cn/img_convert/1d57a0d658c85a64e9334f9e41a11c7b.png#clientId=u60398639-9444-4&from=paste&height=405&id=uec42f753&margin=[object Object]&name=image.png&originHeight=494&originWidth=647&originalType=binary&ratio=1&size=43691&status=done&style=none&taskId=ubbb7b52a-dd97-41d1-9b2e-cea65b2599d&width=531)

2. 内存管理分类

在游戏开发中需要重点管理的内存主要分为两类:

  1. 使用new或者malloc创建的指针
  2. 加载到内存中的纹理

二、指针管理

1. 主动创建的指针

主动创建的指针是指程序开发中使用的结构体,类,数组等,创建于堆内存中的指针,这些指针的创建和释放必须成对出现,例如: 在A类中new 了一个B ,那么B的释放必须要有,常用方式在析构中写delete B

2. 派生自cocos的指针

在cocos中,派生自Ref的类型拥有自动释放的功能,所有Ref对象通过 _referenceCount 来控制对象的释放.
当_referenceCount 为0时,该对象自动释放掉.
代码原型为:

void Ref::release()
{
    CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
    --_referenceCount;

    if (_referenceCount == 0)
    {
        
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
        auto poolManager = PoolManager::getInstance();
//        if(!poolManager->isObjectInPools(this)){
//            retainlist.remove(this);
//        }
        
        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_ENABLE_SCRIPT_BINDING
        ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine();
        if (pEngine != nullptr && pEngine->getScriptType() == kScriptTypeJavascript)
        {
            pEngine->removeObjectProxy(this);
        }
#endif // CC_ENABLE_SCRIPT_BINDING

#if CC_REF_LEAK_DETECTION
        untrackRef(this);
#endif
        delete this;
    }
}

其中 Ref :: retain() 函数可增加该对象的 _referenceCount ,通过 Ref :: release() 函数可以减少 该对象的 _referenceCount.
addchild(obj) 操作会对obj 进行 _referenceCount +1操作, obj->removeFromParent和 removeChild(obj) 会对会对obj 进行 _referenceCount -1操作,
关于 Ref :: autorelease() ,调用该方法后, 该对象会放入 AutoreleasePool 中,在下一帧 会将AutoreleasePool 中的所有对象的 _referenceCount - 1

三、 关于贴图的内存管理

贴图是游戏运行中占用内存的主要因素,通过优化贴图可以起到立竿见影的内存优化.

1. 贴图的优化

纹理优化的目的是让它们占用的内存尽量的小,那么纹理加载进内存后,大小计算公式如下:
纹理内存大小(字节) = 纹理宽度 x 纹理高度 x 像素字节
像素字节 = 像素通道数(R/G/B/A) x 通道大小(1字节/半字节)
从上面公式可以看到,纹理加载进内存后的大小跟尺寸/像素通道数/通道大小都有关系,我们就从它们着手优化,还可以通过提高复用率和合成图集达到优化的目的


游戏中的ui图片通常是比较零碎的元素, 通过UI制作工具将零碎的贴图进行组合.来实现美术素材的复用及不同设备的适配.
零碎的贴图会增加CPU的draw call,极大的影响游戏性能.通常的做法是将图片合并为图集(plist).虽然将贴图元素生成为图集会降低 IO 和 Draw Call ,但不是所有图片都适合制作为图集,我们通常会做一些限制:

  1. 图集的最大尺寸不要超过2048x2048

  2. 图集的宽高应相等,并且是2的n次方

  3. 宽或高超过512像素的贴图不放入图集中

ui的元素尽量拆分得足够细,以保证图片的复用性. 比如背景框可以做成64 x 64 的九宫格图,背景框中的细节单独切图.

2. 贴图的加载方式

通用的元素合并在一个图集中,可以在游戏开始的时候进行加载,单个模块使用到的贴图可以在模块初始化的时候加载,用完之后可以释放掉.
在cocos中图片的加载方式通常有两种:

  1. 单图的加载
Director::getInstance()->getTextureCache()->addImage(texturePath)
  1. 图集加载

SpriteFrameCache加载. 通过将Texture2D放入SpriteFrame中,再将SpriteFrame对象放入SpriteFrameCache

SpriteFrameCache::getInstance()->addSpriteFramesWithFile("res/xxx.plist");

3. 贴图的释放

因为Ref的派生对象的释放需要_referenceCount 为0时才会执行. 因此Texture2D对象的释放需要满足以下条件

  1. 使用到该贴图的Node需要移除并释放掉
  2. 移除SpriteFrameCache中的引用
SpriteFrameCache::getInstance()->removeSpriteFramesFromFile("res/xxx.plist");
  1. 删除TextureCache中的引用
Director::getInstance()->getTextureCache()->removeTextureForKey("res/xxx.png")
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值