资源的动态引用与静态的分别

切场景也可以用过场动画拖一下时间去加载资源。

如果场景显示后再异步加载资源,显示和逻辑都有额外的事情要做,所以我个人习惯都是在场景onLoad前把需要的资源都准备好,场景初始化时候直接使用资源。资源多的时候,过场动画就展示久一些 :rofl:

1赞

嗯嗯我也倾向于这种哈哈

楼上所说的引用计数已经是相对成熟的方案了,确实是支持比较安全的释放动态资源,没有什么不要想了,更没有不支持。汽车一定要自动挡才叫汽车吗?两脚离合不叫支持动态引用?汽车一定要自动驾驶才叫汽车吗?自己踩刹车就不叫支持动态引用?

确实引擎今天在大部分平台上,只能借助引用计数,手动动态释放。这是平台特性决定的,自动释放依赖 JS 虚拟机提供的 FinalizationRegistryWeakRef,目前还未普及。

上面那句话的意思是,引擎支持自动动态释放,但是目前只有少部分平台支持。不信可以看看引擎的代码 engine/cocos/core/data/garbage-collection.ts

实际上去年编辑器已经支持完美的自动动态释放了,不需要考虑静态引用或引用计数(内存泄露另说),因为编辑器所用的 Electron 版本我们是确定的,所以可以开启(偶尔无奈升级 Electron,由于各种平台原因,可能会带来插件兼容性的细微差异,只能权衡利弊,实在抱歉)。

这证明了是平台不支持,不是工具不支持,也不是引擎不支持。
将来,相信随着各大浏览器、小游戏平台的内核升级,最终我们能够告别引用计数。

目前 FinalizationRegistry 和 WeakRef 的平台支持情况

这不是要甩锅,而是想要强调,我们仍然是支持的,只不过现在还是手动挡、人工驾驶,将来会有自动挡、自动驾驶。

4赞

你引擎内部有引用计数这个方式,和使用者能用这个方式 完全是两个问题,
现在的问题是 用户会静态加载资源 也会动态加载资源 各种资源内部交叉有交叉关系 ,现在使用者根本分不出 哪些资源被静态引用了 又被动态引用了,动态静态有交集,你告诉我 我现在想动态释放一个动画 要把个动画相关的所有的资源释放 并且还不影响其他的资源,怎么办?
你们有开放接口吗? 这个不是要让用户自己实现吗?你能说工具支持了?
关于这个动态加载释放动画的问题 你看论坛上有多少了? 各种技巧都用了,就差把你们的引擎代码给读一遍自己写个补丁了 这都能叫实现了?

不懂你讨论的这个动态和静态意义何在。
游戏开发资源管理这个快本来就是需要在设计框架的时候自己去实现一个管理的。
creator算还好了,ab里面至少还有引用计数管理。
unity整套都需要自己去实现。插件另说。
所以,资源管理这块算是游戏开发中的核心之一,引擎不是万能的,不可能什么都给你做的明明白白。
你要是有能力就自己去实现一个插件,然后挂到store去卖。好用,实用,别人就会称赞你。
讨论归讨论,谨言慎行。
官方也不容易,cc3.4开始是处于整合优化的关键时期,压力也是很大的。
多给官方一些耐心。

如果这个动画确定没有其他地方引用的话,可以用 assetManager.releaseAsset(asset) 释放。
如果动画可能也被引用着,那就 asset.decRef(),当动画引用计数为 0 就会自动释放。
释放时会自动对动画引用的子资源做引用计数,释放检查。

确实静态资源是不需要控制引用计数的。但是目前比较大的项目,一般会有一个脚本用于管控所有资源的加载和释放,在这里对外提供一个 addRef 和 decRef。在 defRef 时判断一下对象是否在自身的缓存中即可确保不会对静态资源进行误操作。实际上用户分辨哪些资源是自己加载的,成本还是很低的,只要对自己加载的资源去管理引用就够了。

这里确实还是"不够方便",我们计划在 3.5 先启用原生平台的自动动态释放,原生项目可以先用上。

2dx有一个这个接口 removeUnusedSpriteFrames
释放没有用到资源
3.5的这个功能跟这个接口的功能是一样的吗???
如果是的话
就很方便了
切换完场景
直接调用这个方法
释放掉没有用到的资源

是的,不过不一定会有强制释放(还是取决于平台),可能不会有 removeUnusedXXX 了。就开发者完全不用管释放。

关键问题是 不仅仅有静动交集,还有动动交集,动态加载第一个动画 用到了10个图片资源,动态加载第二个动画 用到了15个图片资源,可能这两个动画其中有3个图片是公用的,现在想释放第二个动画,那么还要不影响第一个动画,你看 怎么做? 你把这些工作交给用户自己去处理? 首先 我目前还没找到一个好的方案都,目前最好的方式 还都是扒你们引擎内部的一些代码 去尝试实现 这里有多少问题 都不好说 ,谁敢用到生产环节中?
这种重要的内存处理的功能,只能你们开放接口来处理 给个明确的使用规则,就像热更新功能一样 至少要有一套完整的解决方式 这个功能 才能叫支持了!

两个动画加载时addRef(),第二个动画释放时用decRef()
我理解这个时候公用的三个图片应该不会被提前释放才对。

我的项目里都是只对顶层资源做addRef() decRef(),依赖的子资源不需要去主动处理。

引擎支持 addRef 和 decRef,看来你没用过是吧?从 2.4 开始就有的方案,建议你先了解一下,看看文档 资源释放 · Cocos Creator

有什么不清晰的可以再询问

不用你建议,你文档上写了的 自然都能看到,
问题是这些工作要用户自己去做吗 要跟踪每个动态动画的每个涉及到的资源? 要手工记录每个涉及到的资源的引用数量,然后再去释放的时候再去遍历找每个资源对应的数量,
首先 第一步你们的文档连每个动画涉及到的子资源 该怎么遍历 都没写出来 ,还要翻遍整个论坛 根据不同的工具的版本 来处理不同的用法,否则论坛上也不会那么多关于怎么动态释放资源的问题了 这个不是你给我一个连个完整的描述细节都没有的文档 就能解决的,
这个工作 应该你们官方出个能用的方案 有个例程,放到那里 让每个人都看到 ,能用的完整的东西 你们做到位了 不会有用户说你们是问题的 ,

看来你可能真的没看文档,楼上那么多开发者不约而同给出的答案是哪来的?哪里还要自己手动遍历子资源?

你这个是完全静态的情况的吧 如果子资源有静动交集的时候 你怎么处理?

但是有个问题

        if(this.node._depPrefab){
            let asset = cc.loader.getDependsRecursively(this.node._depPrefab); 
            for (let i = 0; i < asset.length; i++) {
                var key = asset[i];
                if (typeof key === 'string') key = cc.assetManager.assets.get(key);
                let isBuiltin = cc.assetManager.builtins._assets.find(function (assets) {
                    return assets.find(builtinAsset => builtinAsset === key);
                });
                if (isBuiltin) continue;
                if(key && key._ref <=1 ){
                  cc.assetManager.releaseAsset(key);
                }
            }
            this.node.destroy();
        }

我拿到了当前预制体的所有资源引用,仅仅是把引用计数小于等于1的资源释放,但是第二次重新加载预制体的时候,内存确实是释放了,但是图集会出问题。
这个问题只会在原生上面出现,h5反而是正常的。

具体产生的问题就像是
原本这个预制体下某节点用的是a图片,但是通过释放再加载之后显示的是b图片。会对预制体一些节点产生上述的影响,剩下的一些都是正常的。对游戏逻辑无影响,就是显示问题

你这个是2.4以前的版本吧,2.4以后addref和decref配合使用不需要自己记录引用计数,2.4以前我也是自己维护的一套资源管理,如果b也是动态加载的那么要对b引用的资源+1

不影响,静态的相当于引用计数默认为 1。使用者只要关注自己加载进来的两个动画就好。

这个就是2.4.0的。引用计数是引擎自己记录的。我只是管他释放的时候,有没有释放不应该释放的东西。。

实际上这段代码是某个api调用的时候执行的,我只是单纯地加了1个引用计数判断是否去释放。

decRef就可以释放你为什么还要自己来这么一下

试着调用 decRef,而不是 releaseAsset。