分享一个极简的资源释放解决方案

分享一个极简的资源释放解决方案

本文实现基于Cocos Creator 3D v1.2.0

特点

  • 使用简单,无管理负担。
  • 项目接入成本低,只需要处理对象池部分即可。
  • 性能消耗跟场景上的节点数成正比。

原理


释放逻辑如下:

  1. 遍历场景上所有节点,搜集每个节点上所有组件引用的资源。
  2. 收集所有正在加载中的资源(loader.loadRes、loader.loadResArray)
  3. 遍历资源缓存(loader._cache)。如果当前资源既没有被场景节点引用又不属于加载中则释放。

代码讲解

核心代码如下:


另外,代码中有部分函数是从引擎源码(auto-release-utils.ts)中拷贝出来的,因为引擎没有对外暴露这些函数。

难点在于加载中资源的剔除。大致逻辑如下:首先获取所有加载中的加载队列。然后对加载队列依赖的资源进行递归资源引用查找。

项目接入

  1. 引入脚本文件ResCleaner.ts到项目当中
  2. 检查项目所有使用对象池的地方。如果完全没有使用到对象池直接跳到step4。否则执行step3.
  3. 由于引擎内置的NodePool在节点回收的时候会从场景上移除,导致节点无法被资源清理函数遍历到。所以需要自己开发一个对象池。新实现的对象池需要利用node.active = false来避免使用node.removeFromParent()。样例如下:
    直接使用一个数组存储相同预制体
  4. 调用资源清理函数 ResCleaner.clean()。调用时机建议在场景切换之后,也可以在收到
    内存不足告警之后调用。

释放效果演示

测试代码如下:


执行流程:
在start里面开始加载预制体
加载完成后实例化成节点并添加到场景上
调用资源清理函数
将节点移出场景并销毁
调用资源清理函数
将成员变量spframe对资源的引用置空
再次调用资源清理函数

MainCity场景初始状态
image
spframe初始资源引用


prefabA内容

打印的日志如下:

代码和日志结合起来看

图解一下整个过程:
初始


预制体加载完成成

实例化成节点并添加到场景上

调用资源清理后

节点销毁后

调用资源清理后

调用 this.spframe = null

调用资源清理后

注意事项

  • 节点如果要复用请使用node.active = false替代node.removeFromParent()。
  • 以下几种情况资源引用不会被统计到
    1. 使用非组件脚本引用资源。如果类不是继承自Component,就无法挂在节点上,也就无法被统计到。
    2. 在组件脚本中间接引用资源。出于性能考虑不支持。
    3. 使用组件的静态成员变量引用资源。感觉用的不多所以暂不支持。

更多文章
个人博客: https://blog.csdn.net/u014560101
公众号:
image

26赞

demo资源:ResCleaner.rar (259.1 KB)

1赞

推荐大家也看一下宝爷的资源管理 @111304


2赞

牛皮123456

https://blog.csdn.net/u014560101/category_10500363.html

dalao,先mark顶起

demo不能直接运行吗,切换场景一直报错

什么错误。我的引擎是定制过的。

场景的资源自动释放是有问题的,官方已经修复了。可以自定义引擎然后合入这个补丁
https://github.com/cocos-creator/engine/pull/7619

移植了一个Cocos Creator v2.2.0版本

可以,后面参考下

类似于js本身的垃圾回收。只要场景上有节点引用了这个资源,它就不会被回收掉。

如果hack cc.Component是否可以不用遍历了呢?

理论上是可以的。不过也需要处理正在加载中的资源。大兄弟可以搞一个出来呀

MARK.

战略mark

战略性插眼

Cocos v2.2.2 demo ResCleaner-cocos-v222.zip (446.5 KB)
fix 自动合图未处理的bug
fix 手动创建的spriteFrame未处理的bug
增加白名单机制

你的这篇文章版本,不适用于cocos2.4以上版本,

后续直接升级到3.0