浏览 5912 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-09-11
最后修改:2009-09-15
好在经过几天的努力,发现这个问题也没那么困难。 目前修改了一些Ext组件,使用sIEve检查发现除form节点外,其它的都可以正常释放掉。 (form节点无法释放好像是IE自身的特性) 不过只是想要个是否可行的答案罢了,现在看来貌似是可行的。 自己做的补丁帖在3楼 由于是做非常简单的可行性测试,所以只有部分标准UI组件的修正,有兴趣的可以回帖交流。 ---2009-9-14 -----------------------------传说中的分割线--------------------------------------- 最近准备使用Ext3.0.0开发一个one page one application的网站 预先做了一个测试页面,使用动态加载JS,多Tab切换,无iframe。 界面效率问题不用说了,一直就那样,还勉强可以接受。 但在IE下测试内存占用的时候,发现问题真的很严重。 一个新Tab页面,里面有两Tab,每Tab里有Tree、Grid、Chart、Form面板各一个 每多开一个Tab页,内存占用上升9M左右。关闭Tab页释放的内存也极少 开始以为IE本身特性就是这样(不立即释放内存) 但测试开了20来个Tab页,性能下降了一倍左右,内存占用近200M,关闭Tab页也没有释放几M。 于是使用sIEve来检测,发现每开关一个Tab页,都会出现近100来个孤立(Orphan)Dom节点无法释放。 在开关20来次后,总Dom节点达到了近5K的水平 Dom节点无法释放,肯定是有引用它们的JS对象,这些对象也属于无用数据,应当被清除掉才对。 后来上网查找资料,发现Ext2.2有人写过一个补丁,也是做opoa时发现问题并手工修正的: http://www.extjs.com/forum/showthread.php?t=45782 这个帖的作者是中国人: Location: nanning, china 不过我们要用到Ext3.0的一些新功能,所以不会考虑Ext2.2 并且他好像很久没上过线,也没有其它联系方式可以交流,真的很可惜。 实在查不到可用的解决方案只好自己尝试: 查找各组件的destroy方法,看它是否完全销毁了各种引用 换句话说,看它是否将自己创建的已经无用的对象销毁,是否将自身与其它任何dom节点、组件切断关系。 没有的话就给它打补丁。 接触Ext也没多久,忙了好几天,只修改了几个组件,并且还不知道有没有其它问题。 例如Ext.Panel组件的修改: Ext.override(Ext.Panel,{ onDestroy : function(){ Ext.destroy( this.header, this.tbar, this.bbar, this.footer, this.body, this.bwrap, this.dd ); delete this.header; delete this.tbar; delete this.bbar; delete this.footer; delete this.body; delete this.bwrap; delete this.dd; Ext.Panel.superclass.onDestroy.call(this); } }); 注:原beforeDestroy没有考虑this.dd的销毁,没有彻底清除dom节点,没有最后delete解引用 开始是直接修改beforeDestroy方法的,但发现删除顺序有些问题,造成继承它的一些组件destroy时出错 所以新建了onDestroy方法 这些属性都是在Panel这一层定义的,销毁也应该在这一层进行。 以上示例测试过,发现所有节点都能释放。 另外还尝试过修改Ext.dd、Ext.ux.Portal的释放 由于水平不足,对自己的修改很没信心,就先不帖出来了。 希望这帖子能起到抛砖引玉的作用,大家一起讨论解决方法 (有点私心的说 ) 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-09-11
继续抛砖。。。
今天继续尝试的时候,发现之前没注意到的问题。 释放变量和dom节点,可以选择在destroy/beforeDestroy/onDestroy中进行。 我一直是直接写在destroy方法中 但修改到Ext.Container中,释放this.items,this.layout,this.ownerCt这3个变量时,发现孤立dom节点更多了。 Ext.override(Ext.Container,{ // Ext.BoxComponent destroy : function(){ // add delete this.items; delete this.layout; delete this.ownerCt; Ext.Container.superclass.destroy.call(this); } }); 后面一个个测试发现,Ext.Container中定义的beforeDestroy实际上是在定义的destroy方法之后执行。 由于destroy已经直接解除items引用,导致里面的内容没有经过析构,从而引发了孤立节点。 因为,beforeDestroy的运行,是在Ext.Component的destroy中进行的: destroy : function(){ if(this.fireEvent('beforedestroy', this) !== false){ this.beforeDestroy(); if(this.rendered){ this.el.removeAllListeners(); this.el.remove(); if(this.actionMode == 'container' || this.removeMode == 'container'){ this.container.remove(); } } this.onDestroy(); Ext.ComponentMgr.unregister(this); this.fireEvent('destroy', this); this.purgeListeners(); } }, 而对destroy进行继承后,实际上是先执行Ext.Container.destroy,到后面再执行Ext.Container.superclass.destroy 从而使得出现了这种现象 为了避免再出现这种顺序问题,可以在Ext.Container.destroy开始就执行superclass.destroy 或者,改为重写beforeDestroy。 Ext自身好像没有规划得很明确,什么时候该放在beforeDestroy中,什么时候放在onDestroy中,什么时候直接继承destroy。 所以暂时想了一个规则,用来避免再次发生这种问题: 基类中销毁直接定义destroy方法,并调用onDestroy(如果没有,可设置为Ext.emptyFn) 子类中继承onDestroy,并执行完自己的工作后调用父类的onDestroy(和C++里析构函数执行顺序一样) |
|
返回顶楼 | |
发表时间:2009-09-14
最后修改:2009-09-15
中间还改了一些使用中发现的BUG,另外还参考了Ext3.0.1的部分修正。
(Ext3.0.1是从某个使用正版的网站上偷下来的……) ext-patch.js // ******************************* Bugs fix *********************************** Ext.override(Ext.chart.Chart, { onDestroy: function(){ this.bindStore(null); var tip = this.tipFnName; if(!Ext.isEmpty(tip)){ delete window[tip]; } Ext.chart.Chart.superclass.onDestroy.call(this); } }); Ext.override(Ext.form.TextField,{ autoSize : function(){ if(!this.grow || !this.rendered){ return; } if(!this.metrics){ this.metrics = Ext.util.TextMetrics.createInstance(this.el); } var el = this.el; var v = el.dom.value; var d = document.createElement('div'); d.appendChild(document.createTextNode(v)); v = d.innerHTML; Ext.removeNode(d); d = null; v += ' '; var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin)); this.el.setWidth(w); this.fireEvent('autosize', this, w); } }); Ext.override(Ext.Component, { onRender : function(ct, position){ if(!this.el && this.autoEl){ if(Ext.isString(this.autoEl)){ this.el = document.createElement(this.autoEl); }else{ var div = document.createElement('div'); Ext.DomHelper.overwrite(div, this.autoEl); this.el = div.firstChild; } if (!this.el.id) { this.el.id = this.getId(); } } if(this.el){ this.el = Ext.get(this.el); if(this.allowDomMove !== false){ ct.dom.insertBefore(this.el.dom, position); if (div) { Ext.removeNode(div); div = null; } } } } }); // ************************* improve ********************** (function(){ Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){ var ml = new Ext.Element(document.createElement('div')); document.body.appendChild(ml.dom); ml.position('absolute'); ml.setLeftTop(-1000, -1000); ml.hide(); if(fixedWidth){ ml.setWidth(fixedWidth); } var instance = { getSize : function(text){ ml.update(text); var s = ml.getSize(); ml.update(''); return s; }, bind : function(el){ ml.setStyle( Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing') ); }, setFixedWidth : function(width){ ml.setWidth(width); }, getWidth : function(text){ ml.dom.style.width = 'auto'; return this.getSize(text).width; }, getHeight : function(text){ return this.getSize(text).height; }, // Add by clue destroy : function(){ Ext.destroy(ml); delete ml; } }; instance.bind(bindTo); return instance; }; })(); // ******************************* Memory Release ***************************** Ext.override(Ext.Component,{ onDestroy : function(){ if(this.plugins){ Ext.destroy(this.plugins); } Ext.destroy(this.el); } }); Ext.override(Ext.Panel,{ onDestroy : function(){ Ext.destroy( this.header, this.tbar, this.bbar, this.footer, this.body, this.bwrap, this.dd ); Ext.Panel.superclass.onDestroy.call(this); } }); Ext.override(Ext.dd.DragDrop,{ onDestroy : Ext.emptyFn, destroy : function(){ this.onDestroy(); this.unreg(); } }); Ext.override(Ext.dd.DragSource,{ onDestroy : function(){ Ext.destroy(this.proxy); Ext.dd.DragSource.superclass.onDestroy.call(this); } }); Ext.override(Ext.grid.GridDragZone,{ onDestroy : function(){ Ext.destroy(this.ddel); Ext.grid.GridDragZone.superclass.onDestroy.call(this); } }); Ext.override(Ext.dd.StatusProxy,{ onDestroy : Ext.emptyFn, destroy : function(){ this.onDestroy(); Ext.destroy(this.anim,this.el,this.ghost); } }); Ext.override(Ext.grid.GridView,{ destroy : function(){ if(this.colMenu){ Ext.menu.MenuMgr.unregister(this.colMenu); this.colMenu.destroy(); } if(this.hmenu){ Ext.menu.MenuMgr.unregister(this.hmenu); this.hmenu.destroy(); } this.initData(null, null); this.purgeListeners(); if(this.grid.enableColumnMove){ delete Ext.dd.DDM.locationCache[this.columnDrag.id]; Ext.destroy(this.columnDrag,this.columnDrop); } Ext.fly(this.innerHd).removeAllListeners(); Ext.removeNode(this.innerHd); Ext.destroy( this.el, this.mainWrap, this.mainHd, this.scroller, this.mainBody, this.focusEl, this.resizeMarker, this.resizeProxy, this.activeHdBtn, this.dragZone, this.splitZone, this._flyweight ); Ext.EventManager.removeResizeListener(this.onWindowResize, this); } }); Ext.override(Ext.tree.TreePanel,{ onDestroy : function(){ if(this.rendered){ this.body.removeAllListeners(); Ext.dd.ScrollManager.unregister(this.body); Ext.destroy(this.dropZone,this.dragZone,this.innerCt); } Ext.destroy(this.root); Ext.tree.TreePanel.superclass.onDestroy.call(this); } }); Ext.override(Ext.grid.HeaderDropZone,{ onDestroy : function(){ Ext.destroy(this.proxyTop,this.proxyBottom); Ext.grid.HeaderDropZone.superclass.onDestroy.call(this); } }); Ext.override(Ext.menu.Menu,{ onDestroy : function(){ Ext.destroy(this.el); Ext.menu.MenuMgr.unregister(this); Ext.EventManager.removeResizeListener(this.hide, this); if(this.keyNav) { this.keyNav.disable(); } var s = this.scroller; if(s){ Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom); } this.purgeListeners(); Ext.menu.Menu.superclass.onDestroy.call(this); } }); Ext.override(Ext.TabPanel,{ // Ext.Panel onDestroy : function(){ Ext.destroy( this.stack, this.stripWrap, this.stripSpacer, this.strip, this.edge, this.leftRepeater, this.rightRepeater ); Ext.TabPanel.superclass.onDestroy.call(this); } }); Ext.override(Ext.layout.ColumnLayout,{ destroy : function(){ Ext.destroy(this.innerCt); } }); Ext.override(Ext.form.Field,{ onDestroy : function(){ Ext.destroy(this.errorEl,this.errorIcon); Ext.form.Field.superclass.onDestroy.call(this); } }); Ext.override(Ext.form.TextField,{ onDestroy : function(){ Ext.destroy(this.metrics); if(this.validationTask){ this.validationTask.cancel(); this.validationTask = null; } Ext.form.TextField.superclass.onDestroy.call(this); } }); |
|
返回顶楼 | |
发表时间:2009-09-15
我用的是Ext Gwt[即:Gxt] ... 也碰见效率问题...
我的解决方法是,尽量使用单例模式... 然后自己写清空代码... 这样做后,第一次加载比较慢, 之后会非常快.. 关于内存释放问题一直没研究过,有空研究研究... |
|
返回顶楼 | |
发表时间:2009-09-16
不错的主题,这是EXT做OAOP的致命伤,好像官方一直没有很好地解决这个问题
|
|
返回顶楼 | |
发表时间:2009-09-16
最后修改:2009-09-16
不错的帖子.
3.0.1的官方说明改进了不少,不过搞不到补丁包... btw,楼主的帖子的分类错了,不在EXT下. 论坛有bug,在blog那边修改后,论坛这边的分类会出问题. |
|
返回顶楼 | |
发表时间:2010-04-30
楼主我用你的方法释放panel,使用sIEve查看 好象没什么效果啊,内存还是没释放..
|
|
返回顶楼 | |