最近在使用中,发现了一个内存释放的问题,怎么都找不出原因,想要所以想要咨询一下官方 @panda
首先简单概括一下问题,在新的内存模型下,使用一张单独的图片创建 Sprite 后,无法回收释放图片。
下面是重现的步骤和一些说明(我的理解,可能有错的地方):
- 创建一个
cc.Sprite
this.sprite = new cc.Sprite("some.png");
这里先创建一个 Sprite 对象。
绑定层会 创建一个 native ref 对象,引用计数为1;并为其设置 jsb_RefFinalizeHook
在 GC 的时候会触发 jsb_RefFinalizeHook_finalize
- 添加到场景
this.addChild(this.sprite); // this == layer
这里的 this 是一个普通的 cc.Layer
。
这句话,native 会做两件事情
a. native 的 ref 引用计数 +1 ,目前为 2。
b. native 会反过来调用 js 中的 registerNativeRef
函数,把 sprite 和 layer 建立引用关系,防止被 GC
相当于 layer.__nativeRefs[0] <==> sprite
- 删除 Sprite (不在同一帧进行)
this.sprite.removeFromParent();
这里也会做两件事:
a. native 会调用 releaseScriptObject
,解除 layer.__nativeRefs[0] <==> sprite
的引用关系
b. 引用计数 -1,目前为 1,还不能析构
- 手动 GC ( (不在同一帧进行))
ScriptingCore::forceGC();
这里,应该会触发 sprite 的 jsb_RefFinalizeHook_finalize
,在这里,sprite
被 autorelease
-
下一个 mainloop,sprite 被析构
-
回收 texture
cc.director.purgeCachedData();
这里,removing unused texture some.png
会显示为被回收。
这个是我认为应该发生的正确流程,但是实际上,却不是这么顺利,这个步骤的 4 会发生问题: 无论手动 GC 多少次,sprite 的 jsb_RefFinalizeHook_finalize
也不会被调用。
除非我在第3步,removeFromParent
之后添加:
this.sprite = null;
那么在一下次的 GC 时,sprite jsb_RefFinalizeHook_finalize
才会触发。
这里我的疑问是,如果我有很多这种 引用对象的形式
,那是不是说我必须在想要删除回收资源之前,一个一个的将其设置为 null
?这个显然不太合理。
我也尝试了另一种做法:
- 不调用 removeFromParent,而是调用(模仿 Scene::cleanup 的做法)
ScriptingCore::releaseAllChildrenRecursive
这样做,在 GC 的时候,sprite jsb_RefFinalizeHook_finalize
也会触发,但是这时 sprite 的引用计数是2,还不会被autorelease。而再次调用 GC,sprite jsb_RefFinalizeHook_finalize
就再也不会触发了,因此 sprite 一直是保持计数1 的状态,无法析构。
希望官方解惑一下,是不是我的理解或是哪里的做法出了偏差,谢谢了