CocosCreator内存调试技巧H5版本篇包含如下⼲货
1.JavaScript垃圾回收机制理解
2.CocosCreator内存泄漏排查与管理
3.Chrome内存调试技巧
转载请注明原⽂地址
JavaScript垃圾回收机制理解
js内存策略主要有标记清除和引⽤计数两种
具体由浏览器实现
简单来讲如果有全局变量
window.val = [...]//10000个数字的数组
显然这时val要占⽤⼀部分内存
若重新赋值
window.val = null
在仅有以上两条语句的情况下
之前的数组会被清除掉,所占⽤的内存会⾃动回收
回收不是⽴刻进⾏的,但⼀般来讲也很快
函数中的局部变量在函数结束之后,也会进⼊⾃动回收队列
所以通常js开发只需要关⼼全局变量
并不需要过多担⼼内存问题
当然,结合了游戏引擎之后就不⼀样了!以下会结合例⼦说明
CocosCreator内存泄漏排查与管理
在内存管理⽅⾯
CocosCreator开发H5游戏与原⽣游戏可以说完全不同
H5游戏使⽤js⾃⾝的内存管理策略,就是上⽂所说的
⽽原⽣平台使⽤jsb技术依靠cpp层⾯的引⽤记数来管理节点、纹理等内存,和2dx时代⼀样
区别有多明显呢?
⽐如H5版本中
那边全局变量node会⼀直可⽤,占⽤内存,直到⼿动destroy为⽌
⽽2dx-lua中
node = CCNode:create()
同样是全局变量,但由于create⽅法设置了autoRelease
若不增加引⽤记数(⽐如加⼊场景),那么node只能'存活'⼀帧的时间,下⼀帧就被释放掉了
在H5游戏开发中,绝⼤多数节点创建时作为局部变量
若未加⼊场景,则上下⽂结束后,标记清除等待回收
若加⼊了场景,实际上全局场景队列会保留其引⽤,因此不会被清除
若游戏内设置全局变量保存某节点,且其parent==null,此节点也会常驻内存,可以理解为⽤户有意保留不做销毁
内存问题举例
开发过程中遇到了⼀个造成内存严重泄漏的bug
按钮由⼯⼚⽅法创建,若未加⼊场景,导致内存泄漏
与上⽂节点的创建不同之处在于,按钮注册了on('click',...)或on('touchend'...)回调函数
因此其引⽤被保存在cc.eventManager全局变量中
下⾯通过具体的调试来论证这⼀点
介绍排查内存泄漏问题的基本⽅法
ps.若单纯创建节点,不加⼊场景也不注册点击事件,那么该节点或精灵是可以被⾃动回收的
Chrome内存调试技巧
⾸先升级Chrome⾄最新版本(本⽂使⽤59.0.3071.115)
然后打开'开发者⼯具' 选择Memory页签选择Take heap snapshot
可以看到出⽰内存19.2MB
接下来我们创建5000个按钮节点
不加⼊场景也不保存引⽤
可以看到,我们只为按钮注册了touchend事件
并未保存引⽤或加⼊场景,然⽽页⾯内存激增⾄32.1MB
原因就在于cc.eventManager全局变量保留了每⼀个按钮节点的引⽤
导致按钮节点不会⾃动回收
引擎源码查考
CCNode:on⽅法有这样⼀段
this._touchListener = ate({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: true,
owner: this,
mask: _searchMaskInParent(this),
onTouchBegan: _touchStartHandler,
onTouchMoved: _touchMoveHandler,
onTouchEnded: _touchEndHandler
});
if (CC_JSB) {
this._ain();
}
cc.eventManager.addListener(this._touchListener, this);
newAdded = true;
注意owner就是按钮节点
该引⽤保存在_touchListener中并被加⼊cc.eventManager
⼜经过CCEventManager:_forceAddEventListener加⼊cc.eventManager._listenersMap _forceAddEventListener: function (listener) {
var listenerID = listener._getListenerID();
var listeners = this._listenersMap[listenerID];
if (!listeners) {
listeners = new _EventListenerVector();
cocos creator面试题this._listenersMap[listenerID] = listeners;
}
listeners.push(listener);
if (listener._getFixedPriority() === 0) {
this._setDirty(listenerID, this.DIRTY_SCENE_GRAPH_PRIORITY);
var node = listener._getSceneGraphPriority();
if (node === null)
cc.logID(3507);
this._associateNodeAndEventListener(node, listener);
if (node.isRunning())
} else
this._setDirty(listenerID, this.DIRTY_FIXED_PRIORITY);
},
调⽤_associateNodeAndEventListener时⼜加⼊cc.eventManager._nodeListenersMap _associateNodeAndEventListener: function (node, listener) {
var listeners = this._nodeListenersMap[node.__instanceId];
if (!listeners) {
listeners = [];
this._nodeListenersMap[node.__instanceId] = listeners;
}
listeners.push(listener);
},
内存分析图解
了解了代码来龙去脉之后
我们从内存分析的视⾓来问题
从上图所⽰占有内存最多的Object着⼿
可以看到其中⼀个疑似泄漏内存对象的引⽤如下图
正是通过_forceAddEventListener加⼊的_nodeListenersMap和_listenersMap
在分别打开可以看到引⽤具体所在信息
_nodeListenersMap和_listenersMap都可以到owner
即5000个之中的按钮节点
再详细查其实可以确定其instanceID与我们⽣成时是⼀致的
与查看源码时获得的信息⼀致
关掉Object打开cc_Node
到疑似问题节点
同样可以看到其引⽤关系
验证的最后⼀步
我们在console中输⼊以下语句
清除我们刚刚发现的引⽤
cc.eventManager._listenersMap.__cc_touch_one_by_one._sceneGraphListeners = {}
cc.eventManager._nodeListenersMap = {}
再次计算内存,发现内存回到初始值,5000个按钮节点被释放回收!
总结
遇到具体内存泄漏问题时
往往是从开发者⼯具Memory反应的信息着⼿倒推
到问题代码出现的源头
这次内存调试,发现了我们程序代码中的写法错误
杜绝类似'创建按钮后不使⽤'这样的⾏为之后
游戏的内存状况得到了⼤⼤改善
此外还有⼀点dragonBones使⽤的⼩经验
Factory().clear()
db会cache许多动画数据信息甚⾄可以多⾄近百兆
及时清理也可以解决内存不⾜问题
参考⽂献

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。