- 浏览: 235674 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
月度银墙:
wangxiang243 写道不错 !但是ext4中六月和十二 ...
ext2,3,和4 版本 只显示年月的日期插件 -
38123978:
你好,我最近在看cassandra的性能,我想问一下5000万 ...
Cassandra学习笔记3 -
zhaojinmeng:
您好楼主,extjs5要怎么扩展啊?求指导
ext2,3,和4 版本 只显示年月的日期插件 -
love_wting:
为什么下拉框的表格样式显示了,数据访问json也从数据库中取到 ...
Extjs 下拉grid -
laungcisin:
yangxiutian 写道另外Ext4.2.1还故意把x-m ...
ext2,3,和4 版本 只显示年月的日期插件
要了解javascript的内存泄漏问题,首先要了解的就是javascript的GC原理。
我记得原来在犀牛书《JavaScript: The Definitive Guide》中看到过,IE使用的GC算法是计数器,因此只碰到循环 引用就会造成memory leakage。后来一直觉得和观察到的现象很不一致,直到看到Eric的文章,才明白犀牛书的说法没有说得很明确,估计该书成文后IE升级过算法吧。在IE 6中,对于javascript object内部,jscript使用的是mark-and-sweep算法,而对于javascript object与外部object(包括native object和vbscript object等等)的引用时,IE 6使用的才是计数器的算法。
Eric Lippert在http://blogs.msdn.com/ericlippert/archive/2003/09/17/53038.aspx一文中提到IE 6中JScript的GC算法使用的是nongeneration mark-and-sweep。对于javascript对算法的实现缺陷,文章如是说:
"The benefits of this approach are numerous, but the principle benefit is that circular references are not leaked unless the circular reference involves an object not owned by JScript. "
也就是说,IE 6对于纯粹的Script Objects间的Circular References是可以正确处理的,可惜它处理不了的是JScript与Native Object(例如Dom、ActiveX Object)之间的Circular References。
所以,当我们出现Native对象(例如Dom、ActiveX Object)与Javascript对象间的循环引用时,内存泄露的问题就出现了。当然,这个bug在IE 7中已经被修复了[http://www.quirksmode.org/blog/archives/2006/04/ie_7_and_javasc.html]。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp 中有个示意图和简单的例子体现了这个问题:
< head >
< script language = " JScript " >
var myGlobalObject;
function SetupLeak() // 产生循环引用,因此会造成内存泄露
{
// First set up the script scope to element reference
myGlobalObject =
document.getElementById( " LeakedDiv " );
// Next set up the element to script scope reference
document.getElementById( " LeakedDiv " ).expandoProperty =
myGlobalObject;
}
function BreakLeak() // 解开循环引用,解决内存泄露问题
{
document.getElementById( " LeakedDiv " ).expandoProperty =
null ;
}
</ script >
</ head >
< body onload = " SetupLeak() " onunload = " BreakLeak() " >
< div id = " LeakedDiv " ></ div >
</ body >
</ html >
尤其是当碰到Closure,当我们往Native对象(例如Dom对象、ActiveX Object)上绑定事件响应代码时,一个不小心,我们就会制造出Closure Memory Leak。其关键原因,其实和前者是一样的,也是一个跨javascript object和native object的循环引用。只是代码更为隐蔽,这个隐蔽性,是由于javascript的语言特性造成的。但在使用类似内嵌函数的时候,内嵌的函数有拥有一个reference指向外部函数的scope,包括外部函数的参数,因此也就很容易造成一个很隐蔽的循环引用,例如:
DOM_Node.onevent ->function_object.[ [ scope ] ] ->scope_chain ->Activation_object.nodeRef ->DOM_Node。
[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp]有个例子极深刻地显示了该隐蔽性:
< head >
< script language = " JScript " >
function AttachEvents(element)
{
// This structure causes element to ref ClickEventHandler //element有个引用指向函数ClickEventHandler()
element.attachEvent( " onclick " , ClickEventHandler);
function ClickEventHandler()
{
// This closure refs element //该函数有个引用指向AttachEvents(element)调用Scope,也就是执行了参数element。
}
}
function SetupLeak()
{
// The leak happens all at once
AttachEvents(document.getElementById( " LeakedDiv " ));
}
</ script >
</ head >
< body onload = " SetupLeak() " onunload = " BreakLeak() " >
< div id = " LeakedDiv " ></ div >
</ body >
</ html >
还有这个例子在IE 6中同样原因会引起泄露
function leakmaybe() {
var elm = document.createElement( " DIV " );
elm.onclick = function () {
return 2 + 2 ;
}
}
for ( var i = 0 ; i 10000 ; i ++ ) {
leakmaybe();
}
btw:
关于Closure的知识,大家可以看看http://jibbering.com/faq/faq_notes/closures.html这篇文章,习惯中文也可以看看zkjbeyond的blog,他对Closure这篇文章进行了简要的翻译:http://www.blogjava.net/zkjbeyond/archive/2006/05/19/47025.html。之所以会有这一系列的问题,关键就在于javascript是种函数式脚本解析语言,因此javascript中“函数中的变量的作用域是定义作用域,而不是动态作用域”,这点在犀牛书《JavaScript: The Definitive Guide》中的“Funtion”一章中有所讨论。
http://support.microsoft.com/default.aspx?scid=KB;EN-US;830555中也对这个问题举了很详细的例子。
一些 简单的解决方案
目前大多数ajax前端的javascript framework都利用对事件的管理,解决了该问题。
如果你需要自己解决这个问题,可以参考以下的一些方法:
-
http://youngpup.net/2005/0221010713 中提到:可以利用递归Dom树,解除event绑定,从而解除循环引用:
if (window.attachEvent) { var clearElementProps = [ 'data', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'ondblclick', 'onclick', 'onselectstart', 'oncontextmenu' ]; window.attachEvent("onunload", function() { var el; for(var d = document.all.length;d--;){ el = document.all[d]; for(var c = clearElementProps.length;c--;){ el[clearElementProps[c]] = null; } } }); }
-
而http://novemberborn.net/javascript/event-cache一文中则通过增加EventCache,从而给出一个相对结构化的解决方案
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->/* EventCache Version 1.0
Copyright 2005 Mark Wubben
Provides a way for automagically removing events from nodes and thus preventing memory leakage.
See <http://novemberborn.net/javascript/event-cache> for more information.
This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
*/
/* Implement array.push for browsers which don't support it natively.
Please remove this if it's already in other code */
if (Array.prototype.push == null ){
Array.prototype.push = function (){
for ( var i = 0 ; i < arguments.length; i ++ ){
this [ this .length] = arguments[i];
};
return this .length;
};
};
/* Event Cache uses an anonymous function to create a hidden scope chain.
This is to prevent scoping issues. */
var EventCache = function (){
var listEvents = [];
return {
listEvents : listEvents,
add : function (node, sEventName, fHandler, bCapture){
listEvents.push(arguments);
},
flush : function (){
var i, item;
for (i = listEvents.length - 1 ; i >= 0 ; i = i - 1 ){
item = listEvents[i];
if (item[ 0 ].removeEventListener){
item[ 0 ].removeEventListener(item[ 1 ], item[ 2 ], item[ 3 ]);
};
/* From this point on we need the event names to be prefixed with 'on" */
if (item[ 1 ].substring( 0 , 2 ) != " on " ){
item[ 1 ] = " on " + item[ 1 ];
};
if (item[ 0 ].detachEvent){
item[ 0 ].detachEvent(item[ 1 ], item[ 2 ]);
};
item[ 0 ][item[ 1 ]] = null ;
};
}
};
}(); -
使用方法也很简单:
<script type="text/javascript">function addEvent(oEventTarget, sEventType, fDest){ if(oEventTarget.attachEvent){ oEventTarget.attachEvent("on" + sEventType, fDest); } elseif(oEventTarget.addEventListener){ oEventTarget.addEventListener(sEventType, fDest, true); } elseif(typeof oEventTarget[sEventType] == "function"){ var fOld = oEventTarget[sEventType]; oEventTarget[sEventType] = function(e){ fOld(e); fDest(e); }; } else { oEventTarget[sEventType] = fDest; }; /* Implementing EventCache for all event systems */ EventCache.add(oEventTarget, sEventType, fDest, true); }; function createLeak(){ var body = document.body; function someHandler(){
return body;
};
addEvent(body, "click", someHandler);
};
window.onload = function(){
var i = 500;
while(i > 0){
createLeak();
i = i - 1;
}
};
window.onunload = EventCache.flush;
</script>
-
http://talideon.com/weblog/2005/03/js-memory-leaks.cfm 一文中的方法类似:
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->/*
* EventManager.js
* by Keith Gaughan
*
* This allows event handlers to be registered unobtrusively, and cleans
* them up on unload to prevent memory leaks.
*
* Copyright (c) Keith Gaughan, 2005.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* (CPL) which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/cpl.php
*
* This software is covered by a modified version of the Common Public License
* (CPL), where Keith Gaughan is the Agreement Steward, and the licensing
* agreement is covered by the laws of the Republic of Ireland.
*/
// For implementations that don't include the push() methods for arrays.
if ( ! Array.prototype.push) {
Array.prototype.push = function (elem) {
this [ this .length] = elem;
}
}
var EventManager = {
_registry: null ,
Initialise: function () {
if ( this ._registry == null ) {
this ._registry = [];
// Register the cleanup handler on page unload.
EventManager.Add(window, " unload " , this .CleanUp);
}
},
/* *
* Registers an event and handler with the manager.
*
* @param obj Object handler will be attached to.
* @param type Name of event handler responds to.
* @param fn Handler function.
* @param useCapture Use event capture. False by default.
* If you don't understand this, ignore it.
*
* @return True if handler registered, else false.
*/
Add: function (obj, type, fn, useCapture) {
this .Initialise();
// If a string was passed in, it's an id.
if ( typeof obj == " string " ) {
obj = document.getElementById(obj);
}
if (obj == null || fn == null ) {
return false ;
}
// Mozilla/W3C listeners?
if (obj.addEventListener) {
obj.addEventListener(type, fn, useCapture);
this ._registry.push({obj: obj, type: type, fn: fn, useCapture: useCapture});
return true ;
}
// IE-style listeners?
if (obj.attachEvent && obj.attachEvent( " on " + type, fn)) {
this ._registry.push({obj: obj, type: type, fn: fn, useCapture: false });
return true ;
}
return false ;
},
/* *
* Cleans up all the registered event handlers.
*/
CleanUp: function () {
for ( var i = 0 ; i < EventManager._registry.length; i ++ ) {
with (EventManager._registry[i]) {
// Mozilla/W3C listeners?
if (obj.removeEventListener) {
obj.removeEventListener(type, fn, useCapture);
}
// IE-style listeners?
else if (obj.detachEvent) {
obj.detachEvent( " on " + type, fn);
}
}
}
// Kill off the registry itself to get rid of the last remaining
// references.
EventManager._registry = null ;
}
};使用起来也很简单
<html><head> <script type=text/javascript src=EventManager.js></script> <script type=text/javascript> function onLoad() { EventManager.Add(document.getElementById(testCase),click,hit ); returntrue; } function hit(evt) { alert(click); } </script> </head> <body onload='javascript: onLoad();'> <div id='testCase' style='width:100%; height: 100%; background-color: yellow;'> <h1>Click me!</h1> </div> </body> </html>
- google map api同样提供了一个类似的函数用在页面的unload事件中,解决Closure带来的内存泄露问题。
-
当然,如果你不嫌麻烦,你也可以为每个和native object有关的就阿vascript object编写一个destoryMemory函数,用来手动调用,从而手动解除Dom对象的事件绑定。
-
还有一种就是不要那么OO,抛弃Dom的一些特性,用innerHTML代替appendChild,避开循环引用。详细见http://birdshome.cnblogs.com/archive/2005/02/16/104967.html中的讨论贴。
Cross-Page Leaks
Cross-Page Leaks和下一节提到的Pseudo-Leaks在我看来,就是IE的bug, 虽然MS死皮赖脸不承认:)
大家可以看看这段例子代码:
< head >
< script language = " JScript " >
function LeakMemory() // 这个函数会引发Cross-Page Leaks
{
var hostElement = document.getElementById( " hostElement " );
// Do it a lot, look at Task Manager for memory response
for (i = 0 ; i < 5000 ; i ++ )
{
var parentDiv =
document.createElement( " <div onClick='foo()'> " );
var childDiv =
document.createElement( " <div onClick='foo()'> " );
// This will leak a temporary object
parentDiv.appendChild(childDiv);
hostElement.appendChild(parentDiv);
hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);
parentDiv = null ;
childDiv = null ;
}
hostElement = null ;
}
function CleanMemory() // 而这个函数不会引发Cross-Page Leaks
{
var hostElement = document.getElementById( " hostElement " );
// Do it a lot, look at Task Manager for memory response
for (i = 0 ; i < 5000 ; i ++ )
{
var parentDiv = document.createElement( " <div onClick='foo()'> " );
var childDiv = document.createElement( " <div onClick='foo()'> " );
// Changing the order is important, this won't leak
hostElement.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);
parentDiv = null ;
childDiv = null ;
}
hostElement = null ;
}
</ script >
</ head >
< body
转自:http://04js.cn/content.asp?id=1612
发表评论
-
js原型分析研究
2012-07-23 22:44 1430原型是个很微妙的东西,很多人不是很理解,理解了原型对于 ... -
Ext4.1.0 API中文版V0.4 Beta(持续更新)
2012-07-17 18:40 1075Ext4.1.0 API中文版V0.4 Beta html ... -
记录回答别人的Ext4问题
2012-07-10 13:23 26341:Ext4版本 双击Tab页面页签关闭 来自问题:ht ... -
Extjs4 时分秒日期插件
2012-05-11 13:05 12336插件1: 初始界面 弹出界面 插件代码 见附件 ... -
Ext3 日期范围插件
2012-04-03 17:32 2113先上 效果图有图有真相嘛 使用例子: ... -
Extjs2分页树 带查询功能
2012-03-29 11:37 4165先上效果图: 插件代码见附件 使 ... -
ext2 日期范围控件(2个控件)
2012-03-25 17:39 3002控件1 介绍: 首先加入 Ext.apply( ... -
ext2 grid 封装 (包含增删改查 导入导出等操作)
2012-02-27 14:32 5002最近项目又用到ext 比较 ... -
tree拖拽
2011-09-04 22:09 2420当时遇到如下问题: 拖拽A tree 的a节点到B tree ... -
ExtJS Web应用程序开发指南(第2版)(针对Ext JS 4.0更新)源代码
2011-08-11 14:56 1102ExtJS Web应用程序开发指南(第2版)(针对Ext JS ... -
通过拖拽Grid改变行的顺序
2010-10-08 13:06 3758<script> var meta ... -
使用注解和反射构造ext的grid需要的列模型
2010-09-18 12:03 1668注解类: package xzd; import ... -
ExtJs Grid分页时序号自增的实现
2010-08-23 16:28 3045首先看到网上都是类似 http://www.blogjava. ... -
extjs 自己的小小总结
2010-08-13 21:15 3287总结自己的代码 可能以后会用到 记录一下 方便以后查阅 1 ... -
eclipse 中怎样来集成 JBOSS
2010-08-09 08:46 0eclipse 中怎样来集成 JBOSS -
ext2,3,和4 版本 只显示年月的日期插件
2010-08-03 20:23 15675Ext技术交流群:164648099 1:ext3版本 ... -
关于Extjs3.0中的TreeGrid (Ext.ux.maximgb.tg.EditorGridPanel)(转)
2010-08-01 21:38 4341转:(http://www.liyonghome.cn/ind ... -
ext grid 单元格提示 出现图片
2010-07-31 00:03 2646代码简单 只是记录一下 <html> ... -
ext tree相关知识
2010-07-30 23:53 2601ext tree 动态修改各个节点名称icon小图标 &qu ... -
ext 后台查数据库数据拼装tree数据
2010-07-26 22:21 2472来自:Extjs交流群(164648099) 说明: ...
相关推荐
求文法的closure闭包,针对每个产生式,求其closure闭包,并打印输出
If you're ready to use Closure to build rich web applications with JavaScript, this hands-on guide has precisely what you need to learn this suite of tools in depth. Closure makes it easy for ...
Closure toolsClosure toolsClosure toolsClosure tools
closure-compiler-v20170521.jar,以及一个.chm使用说明:‘Getting Started with the Closure Compiler Application’,‘Advanced Compilation and Externs’,‘Understanding the Restrictions Imposed by the ...
主要完成了数据库存储概念中Closure-Table 存储,写了一个SSM简单的项目 项目是github上的,github也是我自己的项目
Closure Linter完整安装包,包含安装说明。 python-2.7.3.msi setuptools-0.6c11.win32-py2.7.exe
Closure: the definitive guide, by Michael Bolin
Closure Compiler exe 根据[在项目中使用Google Closure Compiler](http://www.cnblogs.com/JeffreyZhao/archive/2009/12/09/ikvm-google-closure-compiler.html)
套工具就是提供一个在JavaScript中做模板的机制及函数库,你可以把HTML布局的部份写成一个*.soy,接着再利用...Closure Templates既可以实现JavaScript,又可以实现Java,所以你可以在服务器端和客户端使用相同的模板。
Closure_ The Definitive Guide js的闭包
补充说明:闭包可以使用USE关键连接外部变量。 总结:PHP闭包的特性其实用CLASS就可以实现类似甚至强大得多的功能,更不能和js的闭包相提并论了吧,只能期待PHP以后对闭包支持...合理使用闭包能使代码更加简洁和精炼。
Closure编译器的纯JavaScript版本,最高级的构建工具
实现机器人运动的闭环检测,是为同时与建图增加约束条件,提高精度
当我们从github上下载了blockly之后,打卡demos下的index.html时,选择blockly-developer-tools时会弹出一个对话框(大体内容是closure dependency not found),此时...放在和blockly同目录下,然后重新选择就好了。
使用Google Closure Compiler js压缩
Laravel开发-closure-table Laravel的邻接表闭包表数据库设计模式实现
前端开源库-closure-loader闭包加载器,用于Google闭包库依赖项的Webpack加载器
关闭webpack插件 此插件支持将Google的关闭工具与webpack一起使用。...您必须同时安装google-closure-compiler软件包和closure-webpack-plugin。 npm install --save-dev closure-webpack-plugin
google cartographer算法对应论文real-time loop closure in 2D LIDAR SLAM
Closure Compiler是用来编译JavaScript的编译器,除了像最常见的JavaScript的压缩机提供的功能,它还会对程序进行分析,把不需要的部份移除,减少的JavaScript程序的大小及提升效率。