构建微信小游戏时,同一个 ImageAsset 会通过 _textureSource 和 _mipmaps[0] 被 Texture2D 引用两次,但 _assignImage() 上传完成后只清理一次依赖和引用,导致 ImageAsset 引用计数残留为 1, 就算CLEANUP_IMAGE_CACHE为true也无法释放。
在微信小游戏中,Image 对象会缓存解码后的图片数据,导致内存过高。
而 Preview 中没有生成_textureSource这条额外的构建依赖,对应图片 UUID 在 Texture2D 依赖列表中只有一次,所以这个问题只在构建产物中明显出现。
是由3.8.7的这条改动引起的:https://github.com/cocos/cocos-engine/pull/18569
Texture2D._textureSource → ImageAsset
Texture2D._mipmaps[0] → ImageAsset
资源加载阶段的 setProperties() 会遍历所有依赖,并为每条依赖执行一次:
depend.owner[depend.prop] = dependAsset.addRef();
所以同一个 ImageAsset 被 addRef() 两次。
而SimpleTexture._assignImage() 只查找并删除第一条匹配的 UUID:
if (macro.CLEANUP_IMAGE_CACHE) {
const deps = dependUtil.getDeps(this._uuid);
const index = deps.indexOf(image._uuid);
if (index !== -1) {
js.array.fastRemoveAt(deps, index);
image.decRef();
}
}
这里存在数量不对称:
setProperties(): addRef × 2
_assignImage(): decRef × 1
结果就是:ImageAsset.refCount: 2 → 1,引用计数始终无法归零。
临时修复方案:
修改 _assignImage() 的依赖清理逻辑:不再只删除第一条对应依赖,而是删除当前 Texture2D 依赖列表中该 ImageAsset UUID 的所有条目,并针对每条被删除的依赖调用一次 decRef() 。
if (macro.CLEANUP_IMAGE_CACHE) {
const dependencies = assetManager.dependUtil.getDeps(this.uuid);
let dependencyIndex = dependencies.indexOf(image.uuid);
// 每删除一条依赖,平衡 setProperties() 中对应的一次 addRef()。
while (dependencyIndex !== -1) {
js.array.fastRemoveAt(dependencies, dependencyIndex);
image.decRef();
dependencyIndex = dependencies.indexOf(image.uuid);
}
}
是不是因为快被收购了所以掉以轻心……