170

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

コードの 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 のプロファイラーを使用して、これが実際に当てはまるかどうかを確認する方法がわかりません。ヒープ プロファイラーのスナップショットには無数のものが表示されますが、何が良いか悪いかをデコードする方法がわかりません。これまでに見たチュートリアルでは、「スナップショット プロファイラーを使用する」ように指示するか、プロファイラー全体がどのように機能するかについて非常に詳細なマニフェストを提供してくれます。プロファイラーをツールとして使用することは可能ですか、それとも全体がどのように設計されているかを本当に理解する必要がありますか?

編集:これらのようなチュートリアル:

Gmail のメモリ リークの修正

開発ツールの使用

私が見たものから、そこにあるより強力な素材の代表です. ただし、3 スナップショット テクニックの概念を紹介する以外に、(私のような初心者にとって) 実践的な知識はほとんど提供されていません。「DevTools の使用」チュートリアルは実際の例では機能しないため、あいまいで一般的な概念の説明はあまり役に立ちません。「Gmail」の例については:

それで、あなたは漏れを見つけました。それで?

  • プロファイル パネルの下半分で、漏れたオブジェクトの保持パスを調べます

  • 割り当て場所を簡単に推測できない場合 (つまり、イベント リスナー):

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

  • 閉鎖を使用していますか?適切な既存のフラグ (つまり、goog.events.Listener.ENABLE_MONITORING) を有効にして、構築中に creationStack プロパティを設定します。

それを読んだ後、私は自分自身がより混乱していることに気づきました。繰り返しになりますが、それは単に物事を行うように指示しているだけであり、方法ではありません。私の見解では、そこにあるすべての情報はあいまいすぎるか、すでにプロセスを理解している人にしか意味をなさない.

これらのより具体的な問題のいくつかは、以下の @Jonathan Naguin の回答で提起されています。

4

9 に答える 9

215

メモリ リークを見つけるための優れたワークフローは、Loreena Lee と Gmail チームがメモリの問題を解決するために最初に使用した3 つのスナップショット手法です。手順は、一般的に次のとおりです。

  • ヒープのスナップショットを取得します。
  • 何かをする。
  • 別のヒープ スナップショットを取得します。
  • 同じことを繰り返す。
  • 別のヒープ スナップショットを取得します。
  • スナップショット 3 の [概要] ビューで、スナップショット 1 と 2 の間に割り当てられたオブジェクトをフィルター処理します。

あなたの例では、スタートボタンのクリックイベントまでバックボーンビューの作成を遅らせるこのプロセスを示すようにコードを調整しました (ここで見つけることができます)。今:

  • HTML (このアドレスを使用してローカルに保存) を実行し、スナップショットを取得します。
  • [開始] をクリックしてビューを作成します。
  • 別のスナップショットを撮ります。
  • 削除をクリックします。
  • 別のスナップショットを撮ります。
  • スナップショット 3 の [概要] ビューで、スナップショット 1 と 2 の間に割り当てられたオブジェクトをフィルター処理します。

これで、メモリ リークを見つける準備が整いました。

いくつかの異なる色のノードに気付くでしょう。赤のノードは Javascript からそれらへの直接参照を持っていませんが、切り離された DOM ツリーの一部であるため、生きています。ツリー内に Javascript から (おそらくクロージャまたは変数として) 参照されるノードがあるかもしれませんが、偶然にも DOM ツリー全体がガベージ コレクションされるのを妨げています。

ここに画像の説明を入力

ただし、黄色のノードには Javascript からの直接参照があります。同じデタッチされた DOM ツリーで黄色のノードを探して、Javascript からの参照を見つけます。DOM ウィンドウから要素につながる一連のプロパティが必要です。

特に、HTML Div 要素が赤でマークされていることがわかります。要素を展開すると、「キャッシュ」関数によって参照されていることがわかります。

ここに画像の説明を入力

行を選択し、コンソールで $0 と入力すると、実際の機能と場所が表示されます。

>$0
function cache( key, value ) {
        // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
        if ( keys.push( key += " " ) > Expr.cacheLength ) {
            // Only keep the most recent entries
            delete cache[ keys.shift() ];
        }
        return (cache[ key ] = value);
    }                                                     jquery-2.0.2.js:1166

これは、要素が参照されている場所です。残念ながら、できることはあまりありません。これは、jQuery の内部メカニズムです。ただし、テスト目的のためだけに、関数に移動してメソッドを次のように変更します。

function cache( key, value ) {
    return value;
}

プロセスを繰り返すと、赤いノードは表示されません:)

ドキュメンテーション:

于 2013-11-01T12:37:21.333 に答える
5

JavaScriptのメモリリークを探すのに大変参考になるGoogleの紹介動画があります。

https://www.youtube.com/watch?v=L3ugr9BJqIs

于 2014-11-14T07:59:59.637 に答える
3

開発者ツールの [タイムライン] タブを確認することもできます。アプリの使用状況を記録し、DOM ノードとイベント リスナーの数を監視します。

メモリ グラフが実際にメモリ リークを示している場合は、プロファイラーを使用してリークの原因を特定できます。

于 2013-10-30T08:35:52.800 に答える