チュートリアル: メモリ リークの検出 (JavaScript)Walkthrough: Find a memory leak (JavaScript)

Windows と Windows Phone に適用Applies to Windows and Windows Phone

このチュートリアルでは、JavaScript メモリ アナライザーを使用し、単純なメモリの問題を特定して修復するプロセスについて説明します。This walkthrough leads you through the process of identifying and fixing a simple memory issue by using the JavaScript memory analyzer. JavaScript メモリ アナライザーは、Visual Studio で、JavaScript を使用して Windows 用に開発された UWP アプリに対して使用できます。The JavaScript memory analyzer is available in Visual Studio for UWP apps built for Windows using JavaScript. このシナリオでは、作成されるのと同じペースで破棄されるはずの DOM 要素がメモリに保持されてしまうアプリを作成します。In this scenario, you create an app that incorrectly retains DOM elements in memory instead of disposing of elements at the same rate in which they are created.

このアプリのメモリ リークの原因は非常に特殊なものですが、以下に示す手順は、一般にメモリがリークしているオブジェクトを分離するのに効果的なワークフローです。Although the cause of the memory leak in this app is very specific, the steps shown here demonstrate a workflow that is typically effective in isolating objects that are leaking memory.

JavaScript メモリ アナライザーのテスト アプリの実行Running the JavaScript memory analyzer test app

  1. Visual Studio で、 [ファイル][新規作成][プロジェクト]の順にクリックします。In Visual Studio, choose File, New, Project.

  2. 左ペインで [JavaScript] を選択し、次に [Windows][Windows 8]の順に選択してから、 [ユニバーサル] または [Windows Phone アプリ]を選択します。Choose JavaScript in the left pane, and then choose Windows, Windows 8, then either Universal or Windows Phone Apps.

    重要

    このトピックに示すメモリ使用量の結果は、Windows 8 アプリでテストされます。The memory usage results shown in this topic are tested against a Windows 8 app.

  3. 中央のペインにある [空のアプリケーション] プロジェクト テンプレートを選択します。Choose the Blank App project template in the middle pane.

  4. [名前] ボックスに JS_Mem_Testerなどの名前を指定し、 [OK]をクリックします。In the Name box, specify a name such as JS_Mem_Tester, and then choose OK.

  5. ソリューション エクスプローラーで default.html を開き、次のコードを <body> タグの間に貼り付けます。In Solution Explorer, open default.html and paste the following code between the <body> tags:

    <div class="wrapper">  
        <div id="item"></div>  
        <button class="memleak" style="display: block" >Leak Memory</button>  
    </div>  
    

    重要

    「Windows 8.1 ユニバーサル アプリ」テンプレートを使用する場合、Windows および.WindowsPhone プロジェクトの両方で HTML および CSS コードを更新する必要があります。If you are using a Windows 8.1 universal app template, you need to update HTML and CSS code in both the .Windows and the .WindowsPhone projects.

  6. default.css を開き、次の CSS コードを追加します。Open default.css and add the following CSS code:

    .memleak {  
        position: absolute; top: 100px; left: 100px;  
    }  
    
  7. default.js を開き、すべてのコードを次のコードに置き換えます。Open default.js and replace all the code with this code:

    (function () {  
        "use strict";  
    
        var app = WinJS.Application;  
        var activation = Windows.ApplicationModel.Activation;  
    
        var wrapper;  
        var elem;  
    
        app.onactivated = function (args) {  
            if (args.detail.kind === activation.ActivationKind.launch) {  
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {  
                } else {  
                }  
                args.setPromise(WinJS.UI.processAll());  
    
                elem = document.getElementById("item");  
                wrapper = document.querySelector(".wrapper");  
                var btn = document.querySelector(".memleak");  
                btn.addEventListener("click", btnHandler);  
                run();  
            }  
        };  
    
        app.oncheckpoint = function (args) {  
        };  
    
        app.start();  
    
        function run() {  
            initialize();  
            load();  
        }  
    
        function initialize() {  
    
            if (wrapper != null) {  
                elem.removeNode(true);  
            }  
        }  
    
        function load() {  
    
            var newDiv = document.createElement("div");  
    
            newDiv.style.zIndex = "-1";  
            newDiv.id = "item";  
    
            wrapper.appendChild(newDiv);  
        }  
    
        function btnHandler(args) {  
            run();  
        }  
    
    })();  
    
  8. F5 キーを押してデバッグを開始します。Choose the F5 key to start debugging. [Leak Memory] ボタンがページに表示されることを確認します。Verify that the Leak Memory button appears on the page.

  9. Visual Studio に戻り (Alt + Tab キー)、Shift キーを押しながら F5 キーを押してデバッグを停止します。Switch back to Visual Studio (Alt+Tab), and then choose Shift+F5 to stop debugging.

    アプリが動作することは確認できたので、次にメモリの使用量を確認します。Now that you've verified that the app works, you can examine the memory usage.

メモリの使用量の分析Analyzing the memory usage

  1. [デバッグ] ツール バーの、 [デバッグの開始] の一覧で、更新されたプロジェクトのデバッグ対象を選択します (Windows Phone エミュレーターまたは [シミュレーター]のいずれか)。On the Debug toolbar, in the Start Debugging list, choose the debug target for the updated project: either one of the Windows Phone Emulators or Simulator.

    ヒント

    UWP アプリの場合、この一覧で [ローカル コンピューター] または [リモート コンピューター] を選択することもできます。For a UWP app, you can also choose Local Machine or Remote Machine in this list.

  2. [デバッグ] メニューの [パフォーマンス プロファイラー...]をクリックします。On the Debug menu, choose Performance Profiler....

  3. [使用可能なツール][JavaScript メモリ]を選択し、 [開始]をクリックします。In Available Tools, choose JavaScript Memory, and then choose Start.

    このチュートリアルでは、メモリ アナライザーをスタートアップ プロジェクトにアタッチします。In this tutorial, you'll be attaching the memory analyzer to the startup project. インストールしたアプリへのメモリ アナライザーのアタッチなど、その他のオプションについては、以下を参照してください。 [JavaScript メモリ]の順にクリックします。For info about other options, like attaching the memory analyzer to an installed app, see JavaScript Memory.

    メモリ アナライザーを起動したとき、VsEtwCollector.exe を実行するアクセス許可を要求するユーザー アカウント制御が表示される場合があります。When you start the memory analyzer, you might see a User Account Control requesting your permission to run VsEtwCollector.exe. [はい]をクリックします。Choose Yes.

  4. Leak Memory を 4 回続けてクリックします。Choose the Leak Memory button four times in succession.

    ボタンをクリックすると、default.js のイベント処理コードで、メモリ リークを引き起こす処理が実行されます。When you choose the button, the event handling code in default.js does work that will result in a memory leak. これを診断用に使用します。You'll use this for diagnostic purposes.

    ヒント

    メモリ リークをテストするシナリオを繰り返すことによって、アプリの初期化中やページの読み込み時にヒープに追加されるオブジェクトなど、必要のない情報を簡単に除去できます。Repeating the scenario that you want to test for a memory leak makes it easier to filter out uninteresting info, such as objects that are added to the heap during app initialization or when loading a page.

  5. 実行中のアプリから Visual Studio に切り替えます (Alt + Tab キーを押します)。From the running app, switch to Visual Studio (Alt+Tab).

    JavaScript メモリ アナライザーの情報が Visual Studio の新しいタブに表示されます。The JavaScript memory analyzer displays information in a new tab in Visual Studio.

    この概要ビューのメモリ グラフには、メモリの使用量の変化が表示されます。The memory graph in this summary view shows process memory usage over time. このビューには、 [ヒープ スナップショットの作成]などのコマンドも用意されています。The view also provides commands like Take heap snapshot. 特定の時刻におけるメモリの使用量の詳細情報がスナップショットに示されます。A snapshot provides detailed information about memory usage at a particular time. 詳細については、「 [JavaScript メモリ]の順にクリックします。For more info, see JavaScript Memory.

  6. [ヒープ スナップショットの作成]をクリックします。Choose Take heap snapshot.

  7. アプリに切り替え、 [Leak Memory]をクリックします。Switch to the app and choose Leak Memory.

  8. Visual Studio に切り替え、 [ヒープ スナップショットの作成] をもう一度クリックします。Switch to Visual Studio and choose Take heap snapshot again.

    次の図は、ベースライン スナップショット (#1) とスナップショット #2 を示しています。This illustration shows the baseline snapshot (#1) and Snapshot #2.

    ベースライン スナップショットとスナップショット 2The baseline snapshot and snapshot 2

    注意

    Windows Phone エミュレーターでは、スナップショットが取得されたときにアプリのスクリーンショットが表示されません。The Windows Phone Emulator does not show a screenshot of the app at the time the snapshot was taken.

  9. アプリに切り替え、もう一度 [Leak Memory] をクリックします。Switch to the app and choose the Leak Memory button again.

  10. Visual Studio に切り替え、 [ヒープ スナップショットの作成] をもう一度クリックします。Switch to Visual Studio and choose Take heap snapshot for the third time.

    ヒント

    このワークフローの 3 番目のスナップショットを取得することにより、ベースラインのスナップショットと 2 番目のスナップショットの間での、メモリ リークと関連しない変更を除外できます。By taking a third snapshot in this workflow, you can filter out changes from the baseline snapshot to the second snapshot that aren't associated with memory leaks. たとえば、ページのヘッダーやフッターの更新などがある場合、メモリ使用量には変化があっても、メモリ リークには無関係である場合があります。For example, there may be expected changes such as updating headers and footers on a page, which will generate some changes in memory usage but may be unrelated to memory leaks.

    次の図は、スナップショット #2 とスナップショット #3 を示しています。This illustration shows Snapshot #2 and Snapshot #3.

    スナップショット 2 とスナップショット 3Snapshot 2 and snapshot 3

  11. Visual Studio で、 [停止] をクリックしてプロファイリングを停止します。In Visual Studio, choose Stop to stop profiling.

  12. Visual Studio でスナップショットを比較します。In Visual Studio, compare the snapshots. [スナップショット #2] には次の情報が示されています。Snapshot #2 shows the following:

    • ヒープ サイズ (左側の赤い上矢印) は、スナップショット #1 に比べて数 KB 増加しています。The heap size (shown by the red up arrow on the left) has increased by several KB compared to Snapshot #1.

      重要

      ヒープ サイズの正確なメモリ使用量の値はデバッグ対象によって異なります。Exact memory usage values for the heap size depend on the debug target.

    • ヒープのオブジェクト数 (右側の赤い上矢印) も、スナップショット #1 に比べて増加しています。The number of objects on the heap (shown by the red up arrow on the right) has increased compared to Snapshot #1. 1 つのオブジェクトが追加され (+1)、削除されたオブジェクトはありません (-0)。One object has been added (+1) and no objects have been removed (-0).

      [スナップショット #3] には次の情報が示されています。Snapshot #3 shows the following:

    • ここでも、ヒープ サイズはスナップショット #2 に比べて数百バイト増加しています。The heap size has increased again by several hundred bytes compared to Snapshot #2.

    • ヒープのオブジェクト数もスナップショット #2 に比べて増加しています。The number of objects on the heap has increased again compared to Snapshot #2. 1 つのオブジェクトが追加され (+1)、削除されたオブジェクトはありません (-0)。One object has been added (+1) and no objects have been removed (-0).

  13. スナップショット #3 で、右側のリンク テキスト (赤い上矢印の横の +1 / -0 という値) を選択します。In Snapshot #3, choose the link text on the right, which shows a value of +1 / -0 next to the red up arrow.

    ヒープ オブジェクトの別のビューへのリンクLink to different view of heap objects

    ヒープのオブジェクトの差分ビュー ( [スナップショット #3 - スナップショット #2]) が開き、既定で種類ビューが表示されます。This opens a differential view of the objects on the heap, called Snapshot #3 - Snapshot #2, with the Types view showing by default. 既定で、スナップショット #2 とスナップショット #3 の間でヒープに追加されたオブジェクトの一覧が表示されます。By default, you see a list of objects added to the heap between Snapshot #2 and Snapshot #3.

  14. スコープ フィルターで、 スナップショット #2 から残されたオブジェクトを選択します。In the Scope filter, choose Objects left over from Snapshot #2.

  15. 次に示すように、オブジェクト ツリーの最上位にある HTMLDivElement オブジェクトを開きます。Open the HTMLDivElement object at the top of the object tree as shown here.

    ヒープ上のオブジェクト数に関する差分ビューDiff view of the object count on the heap

    このビューには、次のような、メモリ リークに関する有用な情報が表示されます。This view shows helpful information about the memory leak, such as the following:

    • このビューには、 itemの ID と共に DIV 要素が表示され、オブジェクトの保持サイズは数百バイトです (正確な値は異なります)。This view shows a DIV element with an ID of item, and the retained size for the object is several hundred bytes (exact value will vary).

    • このオブジェクトは、スナップショット #2 から残されたオブジェクトで、メモリ リークの可能性があるものを表しています。This object is a leftover object from Snapshot #2 and represents a potential memory leak.

      ここでこのアプリについて少し説明しておくと、 Leak Memory をクリックすると DIV 要素が削除され、要素の追加されます。したがって、このコードは正しく動作していないと思われます (つまり、メモリがリークする)。Some knowledge of the app helps at this point: Choosing the Leak Memory button should remove a DIV element and add an element, so the code doesn't seem to be working right (that is, it leaks memory). 次のセクションでは、その修正方法について説明します。The next section explains how to fix that.

    ヒント

    Global オブジェクトに対する相対的な位置を特定することによってオブジェクトを識別できる場合があります。Sometimes, locating an object in relation to the Global object may help identify that object. これを行うには、その識別子のショートカット メニューを開き、 [ルート ビューで表示]をクリックします。To do this, open the shortcut menu for the identifier, and then choose Show in roots view.

メモリの問題の修正Fixing the memory issue

  1. プロファイラーで公開されたデータを使用して、"項目" の ID と共に DOM 要素の削除を担当するコードを確認します。Using data revealed by the profiler, you examine code that is responsible for removing DOM elements with an ID of "item". これは initialize() 関数で発生します。That occurs in the initialize() function.

    function initialize() {  
    
        if (wrapper != null) {  
            elem.removeNode(true);  
        }  
    }  
    

    elem.removeNode(true) は、おそらく、正常に動作していません。elem.removeNode(true) is, perhaps, not working correctly. コードが DOM 要素をキャッシュして、問題を見つける方法を確認します。キャッシュされた要素への参照は更新されていません。You examine how the code is caching the DOM element and find an issue; the reference to the cached element is not getting updated.

  2. default.js で、load 関数の appendChildの呼び出しの直前に次のコード行を追加します。In default.js, add the following line of code to the load function, just before calling appendChild:

    elem = newDiv;  
    

    このコードは、キャッシュされた要素への参照を更新します。これにより、 Leak Memory をクリックすると要素が正しく削除されるようになります。This code updates the reference to the cached element so that the element is correctly removed when you choose the Leak Memory button. load 関数の完全なコードは次のようになります。The complete code for the load function now looks like this:

    function load() {  
    
        wrapper = document.querySelector(".wrapper");  
    
        var newDiv = document.createElement("div");  
    
        newDiv.style.zIndex = "-1";  
        newDiv.id = "item";  
        elem = newDiv;  
    
        wrapper.appendChild(newDiv);  
    }  
    
  3. [デバッグ] メニューの [パフォーマンスと診断]をクリックします。On the Debug menu, choose Performance and Diagnostics.

  4. [使用可能なツール][JavaScript メモリ]を選択し、 [開始]をクリックします。In Available Tools, choose JavaScript Memory, and then choose Start.

  5. 前と同じ手順に従って 3 つのスナップショットを取得します。Follow the same procedure as before to take three snapshots. 手順は次のとおりです。The steps are summarized here:

    1. アプリで Leak Memory を 4 回続けてクリックします。In the app, choose the Leak Memory button four times in succession.

    2. Visual Studio に切り替え、 [ヒープ スナップショットの作成] をクリックしてベースライン スナップショットを取得します。Switch to Visual Studio and choose Take heap snapshot for the baseline snapshot.

    3. アプリで [Leak Memory] をクリックします。In the app, choose the Leak Memory button.

    4. Visual Studio に切り替え、 [ヒープ スナップショットの作成] をクリックして 2 番目のスナップショットを取得します。Switch to Visual Studio and choose Take heap snapshot for the second snapshot.

    5. アプリで [Leak Memory] をクリックします。In the app, choose the Leak Memory button.

    6. Visual Studio に切り替え、 [ヒープ スナップショットの作成] をクリックして 3 番目のスナップショットを取得します。Switch to Visual Studio and choose Take heap snapshot for the third snapshot.

      スナップショット #3 の表示が、ヒープ サイズは " 増加なし " (スナップショット #2 からのヒープ サイズの増加はなし)、オブジェクトの数は "+1/-1" (1 つのオブジェクトが追加され、1 つのオブジェクトが削除された) になります。Snapshot #3 now shows the heap size as No increase from Snapshot #2, and the object count as +1 / -1, which indicates that one objects has been added and one object has been removed. これは期待した動作です。This is the desired behavior.

      次の図は、スナップショット #2 とスナップショット #3 を示しています。The following illustration shows Snapshot #2 and Snapshot #3.

      修正されたメモリ リークを示すスナップショットSnapshots showing the fixed memory leak

関連項目See Also

JavaScript メモリJavaScript Memory