チュートリアル: メモリ リークの検出 (JavaScript)
このトピックでは、JavaScript メモリ アナライザーを使用し、単純なメモリの問題を特定して修復するプロセスについて説明します。チュートリアルで、データの大きな配列を生成するアプリを作成します。このアプリでは、新しいページに移動したときにデータを破棄するようにします。
注意
JavaScript メモリ アナライザーは、Visual Studio 2012 更新プログラム 1 で Windows 8 に対して使用できます。
JavaScript メモリ アナライザーのテスト アプリの実行
Visual Studio で、[ファイル]、[新規作成]、[プロジェクト] の順にクリックします。
左ペインで [JavaScript] をクリックし、中央のペインで [ナビゲーション アプリケーション] をクリックします。
[名前] ボックスに "JS_Mem_Tester" などの名前を入力し、[OK] をクリックします。
ソリューション エクスプローラーで、pages\home フォルダー内の home.html を開き、次のコードを <body> タグの間に貼り付けます。
<div class="fragment homepage"> <header aria-label="Header content" role="banner"> <button class="win-backbutton" aria-label="Back" disabled type="button"></button> <h1 class="titlearea win-type-ellipsis"> <span class="pagetitle">Welcome to JSMemTester!</span> </h1> </header> <section aria-label="Main content" role="main"> <p>Start generating data...</p> <button class="startButton" title="start" >Start</button> <p class="statusMsg1">""</p> <p>Navigate to page... (reload)</p> <button class="navButton" title="navigate" >Navigate</button> </section> </div>
home.js を開き、すべてのコードを次のコードに置き換えます。
(function () { "use strict"; var data; WinJS.UI.Pages.define("/pages/home/home.html", { // This function is called whenever a user navigates to this page. It // populates the page elements with the app's data. ready: function (element, options) { // TODO: Initialize the page here. var firstElem = document.querySelector('.startButton'); firstElem.addEventListener('click', sButtonClicked.bind(this)); var secondElem = document.querySelector('.navButton'); secondElem.addEventListener('click', nButtonClicked.bind(this)); }, generateData: function () { data = {}; var x = 0; var newData = "1"; for (var i = 0; i < 300; i++) { data[i] = "data" + newData; newData = newData + (100 * set[x]).toString(); if (i == 100) { x = 1; } if (i == 200) { x = 2; } } } }); function sButtonClicked(args) { this.generateData(); var elem = document.querySelector('.statusMsg1'); elem.textContent = "Done generating data (string array)."; } function nButtonClicked(args) { WinJS.Navigation.navigate('/pages/home/home.html'); } // Adding arbitrary values to sample data. var mod1 = 10; var mod2 = 100; var mod3 = 1000; var set = [mod1, mod2, mod3 ]; })();
F5 キーを押してデバッグを開始します。このページが表示されることを確認してください。
Visual Studio に戻り (Alt + Tab キー)、Shift キーを押しながら F5 キーを押してデバッグを停止します。
アプリが動作することは確認できたので、次にメモリの使用量を確認します。
メモリの使用量の分析
[デバッグ] ツール バーで、[デバッグの開始] ボックスの一覧の [シミュレーター] をクリックします。
この一覧では [ローカル コンピューター] または [リモート コンピューター] をクリックすることもできます。ただし、シミュレーターの場合は、Visual Studio の横に配置することで、実行中のアプリと JavaScript メモリ アナライザーを簡単に切り替えることができます。詳細については、「Visual Studio からの Windows ストア アプリの実行」および「リモート コンピューターでの Windows ストア アプリの実行」を参照してください。
[デバッグ] メニューの [JavaScript メモリ分析] をポイントし、[Launch Startup Project] (スタートアップ プロジェクトを起動) をクリックします。
このチュートリアルでは、メモリ アナライザーをスタートアップ プロジェクトにアタッチします。インストールしたアプリへのメモリ アナライザーのアタッチなど、その他のオプションについては、「メモリ使用量データの分析 (JavaScript)」を参照してください。
メモリ アナライザーを起動したとき、VsEtwCollector.exe を実行するアクセス許可を要求するユーザー アカウント制御が表示される場合があります。[はい] をクリックします。
実行中のアプリから Visual Studio に切り替えます (Alt + Tab キーを押します)。
JavaScript メモリ アナライザーの情報が [診断ハブ] タブに表示されます。
この概要ビューのメモリ グラフには、メモリの使用量の変化が表示されます。このビューには、[ヒープ スナップショットの作成] などのコマンドも用意されています。特定の時刻におけるメモリの使用量の詳細情報がスナップショットに示されます。詳細については、「メモリ使用量データの分析 (JavaScript)」を参照してください。
[ヒープ スナップショットの作成] をクリックします。
アプリに切り替え、[開始] ボタンをクリックします。
[開始] をクリックすると、home.js のコードによって大きな配列が生成されます。これを診断用に使用します。
Visual Studio に切り替え、[ヒープ スナップショットの作成] をもう一度クリックします。
次の図では、概要ビューの下部のペインにスナップショットが 2 つ示されています。
これらのスナップショットを比較できます。[スナップショット #2] には次の情報が示されています。
ヒープ サイズ (青い文字、左側) は大幅に増加し、1 MB を超えました。
前のスナップショットのヒープ サイズとの差 (赤いテキスト、左側) は 400 KB を超えています。
ヒープのオブジェクト数 (青い文字、右側) は、数百増加し、3,900 を超えました。
前のスナップショットのヒープのオブジェクト数との差 (赤い文字、右側) は 300 を超えています。
[スナップショット #2] で、左側の赤い文字 ("+404 KB" のような差分値を示す) をクリックします。
[スナップショット #2 - スナップショット #1] という名前の差分ビューが開き、既定でドミネーター ビューが表示されます。次の図は、このビューを示しています。
このビューでは、最も多くのメモリを保持するオブジェクトを先頭に、メモリを保持するオブジェクトの一覧が表示されます。JavaScript メモリ アナライザーは、既定で、Windows ランタイムと JavaScript 用 Windows ライブラリによって作成される組み込みオブジェクトを除外します。これにより、アプリ関連のコードの情報が集中的に表示されます。
data オブジェクトに 400 KB を超える [保持サイズの相違] の値があることを確認できます。
ヒント
目的のオブジェクトまたは識別子が見つからないときには、一部のビューにある [名前フィルター] ボックスに識別子の名前を入力することで目的の識別子を探し、選択することができます。
識別子の一覧で、data 識別子を右クリックし、[ルート ビューで表示] をクリックします。
選択された値は、[スナップショット #2 - スナップショット #1] 差分ビューのルート ビューに表示されます。このルート ビューには、調べているオブジェクトが Global オブジェクトに関連して参照される場所が示されます。これは、メモリの問題が発生している場所を特定するために役立つ場合があります。次の図は、この時点でのルート ビューを部分的に示しています (ツリーの最上位の Global オブジェクトは示されていません)。
ルート ビューで、data 変数がホーム ページの ready 関数に呼び出される匿名関数により参照されていることと、これが winControl オブジェクトを含む DIV 要素をルートとしていることを確認できます。これまでに説明したように、このコントロール オブジェクトはアプリの [開始] ボタンを参照しています。
アプリに切り替え、[移動] ボタンをクリックします。
[移動] ボタンをクリックすると新しいページに移動します (アプリが複雑にならないように、ここではホーム ページが再読み込みされるだけです)。
Visual Studio に切り替え、[ヒープ スナップショットの作成] をクリックします。
[スナップショット #3] では、前のスナップショットに比べて、ヒープ サイズおよびヒープのオブジェクト数が大きく変化していないことを確認できます。ここでスナップショットは次のようになります。
このチュートリアルでは、[開始] をクリックしてアプリによって生成されたデータ (配列) が、[移動] をクリックして新しいページに移動 (このケースでは再読み込み) するときに破棄されることを目的にしています。ところがデータが破棄されないため、次に、その問題を解決します。
概要ビューの [停止] をクリックします。
メモリの問題の修正
home.js で、[移動] ボタン用のイベント処理コードを削除します。
function nButtonClicked(args) { WinJS.Navigation.navigate('/pages/home/home.html'); }
次のコードに置き換えます。
function nButtonClicked(args) { data = null; WinJS.Navigation.navigate('/pages/home/home.html'); }
[デバッグ] メニューの [JavaScript メモリ分析] をポイントし、[Launch Startup Project] (スタートアップ プロジェクトを起動) をクリックします。
前のセクションで説明した手順に従い、3 つのスナップショットを取得します。手順は次のとおりです。
Visual Studio に切り替え、[ヒープ スナップショットの作成] をクリックします。
アプリで、[開始] ボタンをクリックします。
Visual Studio に切り替え、[ヒープ スナップショットの作成] をクリックします。
アプリで、[移動] ボタンをクリックします。
Visual Studio に切り替え、[ヒープ スナップショットの作成] をクリックします。
[スナップショット #3] のヒープ サイズが、[開始] をクリックしてデータを生成する前のヒープ サイズに近いことを確認できます。スナップショットを次に示します。
[スナップショット #3] で、ヒープ サイズを示す左側の青い文字をクリックします。
[スナップショット #3] のドミネーター ビューが開きます。これは [スナップショット #3] の差分ビューではなく、詳細ビューです。
[名前フィルター] ボックスに「data」と入力します。
今回は、ヒープに data 変数はありません。つまり、メモリの問題は修正されました。