ChromeでJavaScriptのメモリリークを発見する

私は、Backboneのビューを作成し、イベントにハンドラをアタッチし、ユーザー定義クラスをインスタンス化する非常に単純なテストケースを作成しました。このサンプルで "Remove" ボタンをクリックすると、すべてがクリーンアップされ、メモリリークが発生しないはずです。

このコードの jsfiddle はこちら: http://jsfiddle.net/4QhR2/

// scope everything to a function
function main() {

    function MyWrapper() {
        this.element = null;
    }
    MyWrapper.prototype.set = function(elem) {
        this.element = elem;
    }
    MyWrapper.prototype.get = function() {
        return this.element;
    }

    var MyView = Backbone.View.extend({
        tagName : "div",
        id : "view",
        events : {
            "click #button" : "onButton",
        },    
        initialize : function(options) {        
            // done for demo purposes only, should be using templates
            this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";        
            this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
        },
        render : function() {        
            this.$el.html(this.html_text);

            this.wrapper = new MyWrapper();
            this.wrapper.set(this.$("#textbox"));
            this.wrapper.get().val("placeholder");

            return this;
        },
        onButton : function() {
            // assume this gets .remove() called on subviews (if they existed)
            this.trigger("cleanup");
            this.remove();
        }
    });

    var view = new MyView();
    $("#content").append(view.render().el);
}

main();

しかし、Google Chromeのプロファイラを使って、実際にそうであるかどうかを確認する方法が不明です。ヒーププロファイラのスナップショットに表示される内容は膨大で、何が良くて何が悪いのかが全く分かりません。これまで見たチュートリアルでは、スナップショットプロファイラを使えということだけか、プロファイラ全体がどのように動作するかについて非常に詳細なマニフェストを与えています。プロファイラをツールとして使うだけでいいのでしょうか、それとも全体がどのように設計されているかを理解しなければならないのでしょうか?

EDIT: こんなチュートリアルもあります。

[Gmailのメモリリーク対策】(https://docs.google.com/presentation/d/1wUVmf78gG-ra5aOxvTfYdiLkdGaR9OhXRnOlIcEmu2s/pub?start=false&loop=false&delayms=3000#slide=id.g14717ff3_0_23)

DevToolsの使い方

私が見た限りでは、より強力な素材の代表格です。しかし、3 Snapshot Techniqueの概念を紹介するだけで、(私のような初心者にとって)実践的な知識はほとんど提供されていないと思います。チュートリアルの「DevToolsを使う」は、実例を挙げていないので、曖昧で一般的な概念の説明で、あまり役に立ちません。Gmail'の例については。

***情報漏えいを発見しました。さて、どうする?

・プロファイルパネルの下半分にある、漏れたオブジェクトの保持パスを調べる

-割り当て先が容易に推測できない場合(イベントリスナーなど)。

  • JS コンソールを介して保持オブジェクトのコンストラクタをインストルメントし、割り当てのスタックトレースを保存します。

  • クロージャを使用する?適切な既存のフラグ (例: goog.events.Listener.ENABLE_MONITORING) を有効にして、construction 中に creationStack プロパティを設定します。

これを読んだ後、私自身は混乱が収まるどころか、さらに混乱しています。そしてまた、それは私に物事を do するように言っているだけで、 how するように言っているわけではありません。私の見解では、そこにあるすべての情報は、あまりにも曖昧であるか、すでにプロセスを理解している人にしか意味がないでしょう。

これらの具体的な問題点については、以下の【@Jonathan Naguin's answer】(https://stackoverflow.com/a/19726918/20578)でいくつか指摘されています

jsfiddleのメモリプロファイリングのヒントがあります。jsfiddleの結果を分離するために次のURLを使用します。これはjsfiddleフレームワークをすべて削除し、結果のみをロードします。

http://jsfiddle.net/4QhR2/show/

私は以下のドキュメントを読むまで、メモリリークを追跡するためにタイムラインとプロファイラをどのように使用すればよいのか分かりませんでした。オブジェクト割り当てトラッカーと題されたセクションを読んだ後、私は 'Record Heap Allocations' ツールを使用し、いくつかのDetached DOMノードを追跡することが出来ました。

jQueryのイベントバインディングからBackboneのイベントデリゲーションに変更することで問題を解決しました。私の理解では、新しいバージョンのBackboneは、View.remove()を呼び出すと、自動的にイベントのバインドを解除してくれるそうです。デモのいくつかは、メモリリークを特定するために設定されています。このドキュメントを勉強してもまだ理解できない場合は、ここで遠慮なく質問してください。

https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling

解説 (0)

基本的には、ヒープスナップショット内のオブジェクトの数を確認する必要があります。もし、2つのスナップショットの間にオブジェクトの数が増加し、オブジェクトを破棄したなら、メモリリークが発生しています。私のアドバイスは、あなたのコードでデタッチされないイベントハンドラを探すことです。

解説 (5)

また、デベロッパーツールのタイムラインタブを見ることもできます。アプリの使用状況を記録し、DOM Nodeやイベントリスナーのカウントに注目しましょう。

もし、メモリグラフが本当にメモリリークを示すのであれば、プロファイラを使って何がリークしているのか突き止めることができます。

解説 (0)