常規循環引用內存泄漏和Closure內存泄漏
要了解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.aygfsteel.com/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,從而給出一個相對結構化的解決方案
/* ????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
一文中的方法類似:
/*
?*?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 >
???????? < button?onclick = " LeakMemory() " > Memory?Leaking?Insert </ button >
???????? < button?onclick = " CleanMemory() " > Clean?Insert </ button >
???????? < div?id = " hostElement " ></ div >
???? </ body >
</ html >
LeakMemory和CleanMemory這兩段函數的唯一區別就在于他們的代碼的循序,從代碼上看,兩段代碼的邏輯都沒有錯。
但LeakMemory卻會造成泄露。原因是LeakMemory()會先建立起parentDiv和childDiv之間的連接,這時候,為了讓
childDiv能夠獲知parentDiv的信息,因此IE需要先建立一個臨時的scope對象。而后parentDiv建立了和
hostElement對象的聯系,parentDiv和childDiv直接使用頁面document的scope。可惜的是,IE不會釋放剛才那個臨
時的scope對象的內存空間,直到我們跳轉頁面,這塊空間才能被釋放。而CleanMemory函數不同,他先把parentDiv和
hostElement建立聯系,而后再把childDiv和parentDiv建立聯系,這個過程不需要單獨建立臨時的scope,只要直接使用頁面
document的scope就可以了, 所以也就不會造成內存泄露了
詳細原因,大家可以看看http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp這篇文章。
btw:
IE 6中垃圾回收算法,就是從那些直接"in scope"的對象開始進行mark清除的:
Every variable which is "in scope" is called a "scavenger". A scavenger
may refer to a number, an object, a string, whatever. We maintain a
list of scavengers – variables are moved on to the scav list when they
come into scope and off the scav list when they go out of scope.
Pseudo-Leaks
這個被稱為“秀逗泄露”真是恰當啊:)
看看這個例子:
???? < head >
???????? < script?language = " JScript " >
???????? function ?LeakMemory()
????????{
???????????? // ?Do?it?a?lot,?look?at?Task?Manager?for?memory?response
???????????? for (i? = ? 0 ;?i? < ? 5000 ;?i ++ )
????????????{
????????????????hostElement.text? = ? " function?foo()?{?} " ;//看內存會不斷增加
????????????}
????????}
???????? </ script >
???? </ head >
???? < body >
???????? < button?onclick = " LeakMemory() " > Memory?Leaking?Insert </ button >
???????? < script?id = " hostElement " > function ?foo()?{?} </ script >
???? </ body >
</ html >
MS是這么解釋的,這不是內存泄漏。如果您創建了許多無法獲得也無法釋放的對象,那才是內存泄漏。在這里,您將創建許多元素,Internet Explorer 需要保存它們以正確呈現頁面。Internet Explorer 并不知道您以后不會運行操縱您剛剛創建的所有這些對象的腳本。當頁面消失時(當您瀏覽完,離開瀏覽器時)會釋放內存。它不會泄漏。當銷毀頁面時,會中斷循 環引用。
唉~~~
詳細原因,大家可以看看http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp這篇文章。
其它一些瑣碎的注意點
變量定義一定要用var,否則隱式聲明出來的變量都是全局變量,不是局部變量;
全局變量沒用時記得要置null;
注意正確使用delete,刪除沒用的一些函數屬性;
注意正確使用try...cache,確保去處無效引用的代碼能被正確執行;
open出來的窗口即使close了,它的window對象還是存在的,要記得刪除引用;
frame和iframe的情況和窗口的情況類似。
參考資料
http://jibbering.com/faq/faq_notes/closures.html
http://javascript.weblogsinc.com/2005/03/07/javascript-memory-leaks/
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp
http://72.14.203.104/search?q=cache:V9Bt4_HBzQ8J:jgwebber.blogspot.com/2005/01/dhtml-leaks-like-sieve.html+DHTML+Leaks+Like+a+Sieve+&hl=zh-CN&ct=clnk&cd=9
(這是DHTML Leaks Like a Sieve)一文在google上的cache,原文已經連不上了)
http://spaces.msn.com/siteexperts/Blog/cns!1pNcL8JwTfkkjv4gg6LkVCpw!338.entry
http://support.microsoft.com/default.aspx?scid=KB;EN-US;830555
http://www.ajaxtopics.com/leakpatterns.html
http://blogs.msdn.com/ericlippert/archive/2003/09/17/53028.aspx
http://www.quirksmode.org/blog/archives/2005/02/javascript_memo.html
http://youngpup.net/2005/0221010713
http://blogs.msdn.com/ericlippert/archive/2003/09/17/53038.aspx =
http://support.microsoft.com/kb/266071/EN-US ==>IE 5.0至5.5一些版本中的GC bug
http://www.quirksmode.org/blog/archives/2006/04/ie_7_and_javasc.html ==>ie 7的改進
http://erik.eae.net/archives/2006/04/26/23.23.02/ ==>ie 7的改進
http://www.feedbackarchive.com/spamvampire/today.html ==> Try this script for memory leaks - it leaked 50 megabytes in 15 minutes with firefox on linux:
http://birdshome.cnblogs.com/archive/2005/02/15/104599.html
http://www.quirksmode.org/dom/innerhtml.html
http://www.crockford.com/javascript/memory/leak.html
《JavaScript: The Definitive Guide》4th Edition
http://outofhanwell.com/ieleak/index.php?title=Main_Page
不過這里的“內存溢出”似乎應該是“內存泄漏”。
SetupLeak() 函數中
myGlobalObject = document.getElementById( " LeakedDiv " );
則 myGlobalObject有了一個指向DOM對象 " LeakedDiv " 的引用,
而后
document.getElementById( " LeakedDiv " ).expandoProperty = myGlobalObject;
使得DOM對象 " LeakedDiv " 有一個屬性expandoProperty引用指向myGlobalObject;
這樣myGlobalObject和DOM對象 " LeakedDiv "分別有引用指向對方,所以就循環引用了。
函數BreakLeak() 則清除了DOM對象" LeakedDiv " 向myGlobalObject的引用,
解決循環引用問題。
============================
如果你問的是第二段代碼,那么就比較復雜了。
這涉及到Closure的問題,你可以看看文章中給出的其他鏈接文章,例如zkjbeyond對Closure一文的翻譯。
大致的原因是因為ClickEventHandler()是AttachEvents()的一個內嵌函數,
AttachEvents()函數執行后,把ClickEventHandler()函數綁定在element對象的"onclick"事件上。這樣,element對象就有個間接引用指向ClickEventHandler()函數的一個實例。
又正是因為ClickEventHandler()是一個內嵌函數,因此會為這個實例構造一個scope,這個scope中包括了父函數,也就是AttachEvents()函數中的局部變量和參數,而這兒的參數剛好就是element,所以ClickEventHandler()函數的實例同時又間接引用了element。
說得有些繞.......呵呵
多謝:) 已修正。
不過向毛主席保證,“溢出”一定不是偶翻譯的,哈哈~~
http://www.tgod.com.cn
提供代理功能,推薦大家看看
我見過最好的代理網站
[引用提示]Liu Jian引用了該文章, 地址: http://www.cnblogs.com/LiuJian2005/archive/2008/05/30/1210437.html
document.getElementById( " LeakedDiv " ).expandoProperty =
document.getElementById( " LeakedDiv " )吧.
SetupLeak執行后效果難道不是
domobj.expandoProperty = domobj
myJSObj = domobj;
???
我感覺這根本沒有出現js對象和native對象的循環引用啊,應該只是native對象自身的循環啊.
至于native對象自身循環引用會不會導致內存泄漏我就不知道了....
如果改成
var myGlobalObject = {};
function SetupLeak() // 產生循環引用,因此會造成內存泄露
{
// First set up the script scope to element reference
myGlobalObject.domobj = document.getElementById("LeakedDiv");
// Next set up the element to script scope reference
document.getElementById( " LeakedDiv " ).expandoProperty =
myGlobalObject;
}
那就產生內存泄漏了.
因為在原來的那個代碼中,myGlobalObject只是一個變量,指向domobj,而自身并不是一個jsobject.
這段代碼應該是在微軟官方的文章上改的,不過好像改失敗了,囧...
原文這樣的:
<html>
<head>
<script language="JScript">
function Encapsulator(element)
{
// Set up our element
this.elementReference = element;
// Make our circular reference
element.expandoProperty = this;
}
function SetupLeak()
{
// The leak happens all at once
new Encapsulator(document.getElementById("LeakedDiv"));
}
function BreakLeak()
{
document.getElementById("LeakedDiv").expandoProperty =
null;
}
</script>
</head>
<body onload="SetupLeak()" onunload="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
個人看法,大家繼續討論...被內存泄漏折騰壞了,好像firefox也有.
你想多了:),變量是什么?
你所說“myGlobalObject只是一個變量,指向domobj,而自身并不是一個jsobject.” 沒有錯,但是不是“jsobject”不是重點。
重點在于“IE 6處理不了的是JScript與Native Object(例如Dom、ActiveX Object)之間的Circular References”。
關鍵字是“References引用”:)
你只要考慮一下GC是憑什么來決定是否回收的就可以明白。