问题表现
游戏转换场景,对无用的纹理内存从TextureCache清理,在移除场景节点时,触发了_referenceCount>0
的断言。
这个问题在window上没有复现,在Android上必现,Android触发断言时的调用堆栈如下:
可以看到经历了好几次的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
remove
当前帧结束,会清理auto release pool
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,触发了这个断言,也就暴漏了这个问题。