Cocos Creator 新资源管理系统剖析【四:资源释放】

我打算再做一个管理器来控制释放,就是界面关闭的时候,把要清理的资源,添加到管理器的释放队列,然后做个定时来释放资源

    /** 等待释放的资源uuid */
    private _waitDelete: Map<string, number> = new Map()

    onLoad() {
        //循环进行资源检测
        if (CC_PREVIEW) {
            this.schedule(() => {
                cc.log(assets)
            }, 5)
        }
        if (this.autoRelease) {
            //引擎内置资源常驻
            assets.forEach((asset, uuid) => { asset.addRef() })
            this.schedule(() => {
                this._doRelease()
            }, this.releaseDelay / 2.0)
        }
    }

    private _doRelease() {
        if (this._waitDelete.size) {
            let now = performance.now()
            this._waitDelete.forEach((deleteTick, uuid) => {
                //超过时间进行删除检测
                if (now >= deleteTick) {
                    let asset = assets.get(uuid)
                    if (asset) {
                        //@ts-expect-error
                        cc.assetManager._releaseManager._free(asset)
                      
                    }
                    this._waitDelete.delete(uuid)
                }
            })
        }
    }

    /**
     * 尝试删除(将其放入待删除容器中,真正删除在定时器中调用)
     * @param uuid 资源的uuid
     */
    public tryRelease(uuid: string) {
        let deleteTimeStamp = performance.now() + this.releaseDelay * 1000
        this._waitDelete.set(uuid, deleteTimeStamp)
    }

类似这样的? ResourceManager.ts (github.com)

恩,类似这样的,我记得cocos2dx,有这样的管理器

在js中,没有同时这事情,凡事有先后。
你的情况,

  1. 可能是先打开B界面时,进入了异步加载,判断资源加载过跳过。没进行资源引用,然后关闭A界面。A界面关闭时将本就加载完的资源释放了。

cocos的资源引用,是在资源加载完成时,才进行引用。这样没什么问题。但在这个异步加载的过程中,资源系统对业务(开发者)有什么骚操作是无法感知的,业务也无法对资源系统正在加载的资源进行干预(锁定,加引用)。

可以在上层做粗粒度的资源管理。
加载前就进行了自定义的资源引用。释放时要判断这个自定义的引用是否为0,为0才释放。
如果这样做,用你的情况就会是这样
先打开B界面,引用资源A,资源A引用=2,进入异步加载,判断资源加载过跳过。关闭A界面,资源A引用–,资源A引用=1,不释放。

大概这样。

同时只是表达两个步骤是接连在一起的,也就是同一帧的意思,肯定是会有先后的,这个就不纠结了
这里我是使用了addRef和decRef来做资源管理,至于在什么时候释放,是完全交给引擎处理。
之前在a,b两个界面同时都会动态加载统一个资源时,会出现这种关闭a后打开b的时候,b界面的资源加载出来是已经被释放了的情况。
我刚才想简单做个demo,但是测试的时候,又没问题了,估计还是使用的业务逻辑有问题,这个我后面再好好排查具体那块出问题

请问下,这里的decRef为啥要传false?

// cocos2d/core/asset-manager/utilities.js
        clear (task, clearRef) {
            for (var i = 0, l = task.input.length; i < l; i++) {
                var item = task.input[i];
                if (clearRef) {
                    !item.isNative && item.content && item.content.decRef && item.content.decRef(false);
                }
                item.recycle();
            }
            task.input = null;
        },

你在释放资源A的时,不要在当前帧立马又加载资源A。你得界面应该是关闭打开切换太快的就造成你说的问题

加载新界面资源A资源A是异步的,释放上一个界面资源A又在下一帧。当下一帧释放资源A时,资源加载完成就出现问题咯

嗯,目前就是避免在同一帧,规范下内部开发的一些规范,先规避这样的问题

游戏主界面里面有许多副功能(个人信息,任务…),目前做法是打开副功能加载prefab,关闭释放相关资源,现在遇到个别玩家骚操作:频繁打开关闭副功能,然后游戏卡崩溃了。 定位到问题是虽然释放了资源,但这个只是从缓存中删除,内存并没有立即释放,js垃圾回收是定期自动清理内存, 这个问题有好的解决方法吗

cocos虽然没有马上释放,但是有释放标记的,要么引用计数增加不释放,要么有释放标记不让用的