egret内存泄漏整理学习

原文链接:
https://www.jianshu.com/p/545a27bb985d

为了防止原文被删除,备份学习一下,方便日后查看.如有需求 请移步原文

**

JS内存管理

**

JS中内存的分配和回收都是虚拟机自动完成的,不需要像C/C++为每一个new/malloc操作去写配对的delete/free代码,不容易出现内存泄漏的问题。JS引擎中对变量的存储主要是在栈内存,堆内存。内存泄漏的实质是应该回收的对象出现意外而没有被回收,而是常驻内存。

**

GC原理

**

在堆中查看无用的对象,把这些对象占用的内存空间进行回收。浏览器上的GC(Gabage Collection垃圾回收)实现,大多是采用可达性算法,关于可达性的对象,便是能与GC Roots构成连通图的对象。当一个对象到GC Roots没有任何引用链时,则会成为垃圾回收器的目标,系统会在合适的时候回收它所占的内存。

**

排查方法

**
Google浏览器提供了非常强大的JS调试工具,Head Profiling便是其中一个。它可以记录当前的堆内存快照,并生成对象的描述文件,包括js运行时所用到的所有对象,对象所占用内存大小,引用的层级关系等。

1.打开Google浏览器,打开要监控的网页,win下按F12弹出开发者工具

2.切换到Memory,选择堆类型,选中Take Heap SnapShot开始进行快照

3.右边的视图列出了heap里的对象列表,点击对象可以看到对象的引用层级关系

4.进入游戏后拍下快照,打开某个界面,关闭界面,拍下快照

5.将新的快照转换到Comparsion对比视图,进行内存对比分析

注意:每次拍快照前,都会先自动执行一次GC,保证视图里的对象都是root可及的。GC的触发是依赖浏览器的,所以不能通过时时观察内存峰值而判断是否有内存泄漏。

egret内存泄漏整理学习_第1张图片
egret内存泄漏整理学习_第2张图片
Distance: 对象到根的引用层级距离

**

问题集合

**

  1. 游戏里观察者模式在添加通知后,没有清理监听造成的内存泄漏现象较多。

  2. 特效内存泄漏问题
    添加事件:this.addFloatEffect(this._comCard,“verticalCenter”);
    对象被Tween引用界面关闭的时候移除监听:this.removeFloatEffect(this._imgCard);
    相关文件: ExtendEqmPage.ts
    解决与避免: 修改PanelBase.ts->addFloatEffect,把节点自动加入到缓存列表进行自动管理, 关闭的时候自动移除.

  3. Notify事件内存泄漏问题
    Notify.addEventListener(NotifyType.ROLE_BRIEF_INFO,this._onNotifyRoleBriefInfo,this);
    Notify.removeEventListener(NotifyType.ROLE_BRIEF_INFO,this._onNotifyRoleBriefInfo,this);
    Notify是一个全局的自定义事件分发器,界面的这种监听就必须移除
    相关文件:RankViewBase.ts
    解决与避免: 修改PanelBase.ts, 添加addEvents方法, 规范事件管理.

  4. 特殊事件内存泄漏问题

    this.addEventListener(egret.Event.ENTER_FRAME, this._onEnterFrame, this);
    Event类型若为ENTER_FRAME,RENDER,则必须取消监听,因为这种事件的注册监听是通过一个全局列表来维护,跟点击类型事件不一样, 点击类型事件就算不取消监听,也不会造成内存泄露.
    解决与避免: 使用ENTER_FRAME,RENDER 事件, 记得要移除.

  5. Stage事件内存泄漏问题

    this.stage&&this.stage.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onTouchedStage,this,false,1);
    this.stage不为null时,它是一个全局的舞台,如果界面操作过程中,this.stage为null,可以通过StageManager.getInstance().stage来获取,并移除事件
    解决与避免: 尽量不要注册在this.stage上, 因为onClose的时候, this.stage已经被引擎置空, 应该改成this或者通过StageManager.getInstance().stage取值.

  6. 动态创建子页面的内存泄漏问题

    TowerPanel.ts代码片段:
    let pageInstance = newcqui.god.GodTowerPage();
    this._page.addChild(pageInstance);
    page的基类是PanelBase,在onFirstOpened ()的时候,注册了Notify事件,正常情况下是需要在onClosed()进行移除,但是因为这个page是通过addChild加到父类GodTowerPanel,panel释放的时候,并没有主动调用page的onClosed(),导致泄漏。
    所以需要修改GodTowerPage.ts, 加上释放事件:
    this.addEventListener(egret.Event.REMOVED_FROM_STAGE,()=>{this.onClosed();}, this);
    解决与避免: 在page加一个移除舞台的监听事件,在事件里做其他监听的移除。

  7. 标签Tab内存泄漏问题

    基类是PanelBase,派生类作为UiTab的数据源(即页签),含有UiTab的界面必须要重写onClosed()函数,然后tab调用clear函数去清除数据源。
    或者在页签界面添加一个移除舞台时的监听处理去移除其它监听。
    public onClosed(): void {super.onClosed();this._tab.clear();}相关类: DressPanel, DressSuitPage
    解决与避免: 使用tab的时候要注意调用clear清除数据源.

  8. 定时器内存泄漏问题

    this._countId = egret.setInterval(() => {}, this, 1000);
    需要在界面移除时调用如下函数:
    egret.clearInterval(this._countId);
    解决与避免: 使用egret.setInterval的时候要注意移除.

  9. 动画内存泄漏问题
    动画缓动类,loop属性为true时
    egret.Tween.get(this._imgHand, {loop :true});
    也需要在界面关闭时手动调用:
    egret.Tween.removeTweens(this._imgHand);
    例子:GuideEffect.ts 新手引导特效,被UI界面引用时,导致所有触发引导的界面都会有内存泄漏。UI树上的节点能通过root引用找到时,导致它的父节点也是没法被JS引擎回收。
    解决与避免: 使用egret.Tween的时候要注意移除.

你可能感兴趣的:(egret,相关)