IE7のメモリリーク問題
IE7には、以下の条件を満たす場合メモリリークが発生する問題があります。
- DOMエレメントとJSオブジェクトが循環参照している
- 上記エレメントをスクリプトで削除する
IE8では改修されているようでこの問題は発生しません。また、Firefox 3でも発生しません。
循環参照とメモリリークの例
function test1() { // DOMエレメントとJSオブジェクトの循環参照の例。 // IE6ではこれだけでリークが発生したらしいが、IE7以降では発生しない。 var largeData = createLargeData(); var div = document.getElementById("div"); // DOMエレメント div.foo = function() {}; // JSオブジェクト // closureにより、このオブジェクトはdiv,largeDataの参照を保持する。 // →JSオブジェクトとDOMエレメントの循環参照が発生。 } function test2() { // IE7でリークが発生するパターン。複数回実行するとリークが発生する。 // 以下のように循環参照したエレメントがスクリプトで破棄され、アンロード時に存在しないとGCの対象にならない。 var largeData = createLargeData(); document.getElementById("div").innerHTML = "<div id='created'>created</div>"; // 循環参照したままドキュメントから削除。 var created = document.getElementById("created"); created.foo = function() {}; // IE8ではこの問題は改修されており発生しない。Firefoxでも発生しない。 }
確認はこちら。IE7でtest2のリンクをクリックしていると、使用メモリががしがし増えていきます。IE8,Firefoxでは問題なし。
対策
で、対策ですが、「削除する前に循環参照を切る」とか「そもそも循環参照させない」とかでOK。
function fix1() { // リークを回避するためには、削除の前に参照を切ってやればOK。 var largeData = createLargeData(); var created = document.getElementById("created"); if (created) delete created.foo; // 循環参照を切る。 document.getElementById("div").innerHTML = "<div id='created'>created</div>"; var created = document.getElementById("created"); created.foo = function() {}; } function fix2() { // そもそも循環参照させない。 (function(){ var largeData = createLargeData(); document.getElementById("div").innerHTML = "<div id='created'>created</div>"; return document.getElementById("created"); })().foo = function() {}; }