[√]cocos2dx关于retain release

108 阅读3分钟

问题表现

游戏转换场景,对无用的纹理内存从TextureCache清理,在移除场景节点时,触发了_referenceCount>0的断言。

这个问题在window上没有复现,在Android上必现,Android触发断言时的调用堆栈如下:

image.png

可以看到经历了好几次的release,中间红框的是清理node.children的逻辑,最终出问题的release调用者是一个Texture2D,是一个Sprite在析构的时候,清理对应的Texture2D导致的问题,向上反推,通过log日志,可以得到关于Sprite和Texture2D的一些信息。

排查到这个问题就已经花了好长时间,中间有CC_SAFE_RELEASE(_texture);这个宏导致堆栈看不清,这也是一个坑。

lua_pcall 是来自lua层的调用,也就是说lua层有在调用node:release(),这个node在删除子节点的时候出现的问题,导致问题的根源是清理TextureCache导致的。

引用计数出现了0,意味着该对象已经被delete了,但是为啥会变成0呢?

可能得情况

Node* node1= new Node();// ref1
node1->release();// ref0
node1->release();// 触发断言

还有就是父子关系

Node* parent=new Node();// parent.ref=1
Node* child= new Node();// child.ref=1
parnet->addChild(child);// child.ref=2

parent->relese();// parent.ref=0

  • add

    image.png

  • remove

    image.png

当前帧结束,会清理auto release pool image.png

windows的fill partter

在 Visual Studio 的 Debug 模式下,默认情况下,已释放的堆内存通常会被填充为 0xCC(即十六进制的 0xCC)。这个值被称为 "fill pattern",它有助于标识悬垂指针或访问已释放内存的错误。

unsigned int

  • debug 3722304989(0xDDDDDDDD)

  • release: 11552624

  • std::numeric_limits::max() 4294967295

预设值说明
0xCC上申请的,未初始化的变量,的缺省值,0xCC其实是INT3中断指令
0xCD上申请的,未初始化的变量,的缺省值
0xDD上申请的,所在空间释放后,的缺省值
0xFD在已申请的堆区空间上,设定上下边界值

编译器在执行运行时检查 —— /RTCs, /RTCu, /RTC1,这些会在运行时候自动检查缓冲区溢出

问题定位

在清理TextureCache的时候,内部实现也是进行了一次release,图片纹理真的被释放掉了,此时这个纹理内存已经delete了!

当清理和这个图片有关联的Sprite的时候,Sprite也会清理对应的Texture,但是Sprite->Texture指向的是一块已经释放的内存,Sprite->Texture != nullptr,因为没人告诉Sprite置空纹理指针,虽然纹理内存被释放,但是仍旧可以访问,只是取到的值不可预期。

所以当再次访问纹理的release函数时,在window上_referenceCount可能是一个非常大的值,当然也就触发不了这个断言

void Ref::release(){
    CCASSERT(_referenceCount > 0, "release reference count should be greater than 0");
}

不过在Android上,发现_referenceCount为0,触发了这个断言,也就暴漏了这个问题。