メモリーリークに近い話とは思いますが、ちょっと毛色の違う内容です。 「JavaScript がメモリーを馬鹿食いしてどうしようもない…」 「なぜかメモリーが確保されっぱなしなんだけど…?」 といった状況の解消法。
JavaScript にはガーベッジコレクタがあるから大丈夫… と、思われがちですが、ガーベッジコレクタは『本当にメモリ解放が必要になるまで何もしない』というものです。 (状況が悪いとガーベッジコレクタは何もしてくれなくて、ブラウザが固まってしまいます。。)
「コストはかかるが、信用できないならプログラマが勝手にやってしまえ!!」という感じです。
削除候補のメモリを強制解放 ── CollectGarbage() 関数 ──
解放したいタイミングで window.CollectGarbage() を走らせることで、強制的にメモリを解放できます。
1 2 3 4 | // IE 以外のブラウザ対策のため、関数の存在をチェック if (window.CollectGarbage) { window.CollectGarbage(); } |
この CollectGarbage
関数に関する詳細は、MSDN をご参照ください。
メモリー解放されないコード と その解放
IE で DOM要素 を動的生成した場合、メモリが確保されたままになることがあります。
以下のサンプルでは、 setup_onclick
によってメモリを確保し、
teardown_onclick
によって強制的に解放します。
テスト用 HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | < html > < head > < script type = "text/javascript" > /** * setup ボタン 押下時の処理 */ var setup_onclick = function (event) { var i, elem, frag; // DOM要素 を大量に生成する…だけ。 frag = document.createDocumentFragment(); for (i = 0; i < 5000 ; i++) { elem = document .createElement('div'); frag.appendChild(element); } // 一般的に「解放される」と言われているコード elem = frag = undefined; }; /** * teardown ボタン 押下時の処理 */ var teardown_onclick = function (event) { if (window.CollectGarbage) { window.CollectGarbage(); } }; var window_onload = function () { document.getElementById('setup') .onclick = setup_onclick ; document.getElementById('teardown') .onclick = teardown_onclick ; }; window.onload = window_onload ; </script> </ head > < body > < input type = "button" id = "setup" value = "setup leak" /> < input type = "button" id = "teardown" value = "teardown leak" /> </ body > </ html > |
上記サンプルコード を IE9 で実行した状況を ProcessExplorer で監視した結果
上図グラフの立ち上がりは setup leak ボタンを押下(setup_onclick
を実行)したタイミングで、
グラフの立ち下がりは teardown ボタンを押下(teardown_onclick
を を実行)したタイミングです。
台形の間の時間は約1分(1分以上)です。
少なからず、1分程度では undefined
を設定しても即時解放はされません。
ちなみに、 null
、delete
も同じく即時解放されません。
最後になりましたが…
上記サンプル HTML を IE8 以上で window.open し、setup ボタン押下後、ウィンドウを閉じる、window.openし、setup ボタン押下後、…
を繰り返すと、メモリリークという形で残る場合が見られます。
メモリリークさせるコツは window.open で、ダイアログ表示させる(オプションで toolbar=no とか行う)とか…?
window.CollectGarbage()
を行えば、"ある程度"メモリリークは防げそうです。
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!