2013-06-04 1 views
6

ビューを更新角度のJsでかなり標準的な方法、この例を考える:このAJAXパターンはメモリリークですか?

$scope.fetchResults = function() { 
    // Some local variable that will cause creation of closure 
    var hugeData = serviceX.getMilionRecords(); 

    // Any call to any resource with success and error handlers. 
    $http({ 
     method: "GET", 
     url: "/rest-api/bulk-operation-x", 
     params: { someParam: hugeData.length } 

    }).success(function() { 
     var length = hugeData.length; 
     $scope.reportToUser("Success, that was " + length + " records being processed!"; 

    }).error(function() { 
     var length = hugeData.length; 
     $scope.reportToUser("Something went wrong while processing " + length + " records... :-("; 
    }); 
}; 

これはもちろん仮定の例であるが、それはうまく内からローカル変数の再利用として説明することができるパターンを示しますAJAXコールバック。両方のハンドラ(successerror)におけるもちろん

我々は直接コールバックハンドラから参照されるhugeData上クロージャを作成しています。

私の質問です:AJAX呼び出しの結果は成功または失敗のいずれかのみになる可能性があるため、このコードを再利用するとメモリリークが発生しますか?私は "はい"と答えるだろうが、私は地元のテストでこれを確実に証明することができなかった。

もう少し経験豊かな教師が私にこのことを説明したいと思います。毎日Angularで作業している人からの返信が大好きですが、jqueryの返信も歓迎します。

+2

「メモリリーク」は、割り当てられているメモリを参照し、その後解放されないことを指す非常に特殊な用語です。これは、メモリ管理が手動で行われるコンテキストにのみ適用されます。 JS管理ではプログラマーに透過的であることを考えると、メモリリークは、IEの古いバージョンの場合のように、バグのある実装で特定の状況下でメモリがリークする原因となるコーディングパターンについてのみ関連します。質問が書かれているように意味があるのか​​どうか分かりません。 – Jon

+0

@ Jonのコメントに同意しない。悪い習慣は、グローバルスコープ内のオブジェクトを埋めるようなメモリリークを引き起こす可能性があります。このケースでは、コールバックのいずれかが終了するとhugeデータ変数が削除され($ httpインスタンスがクリーンアップされると最大) – rewritten

+0

@Jon、あなたは非常に簡単に透過的なメモリ管理と任意の言語でメモリリークを作成することができます。 Java、Scala、JavaScript、C#、あなたはその名前を付けます。 @rewritten、これはまさに私が話していることです。 'success()'が終了したときに 'hugeData'が削除されたことをどのように知っていますか?言語の観点からは、 'error()'で使われたクロージャは、後である程度後で実行されるかもしれません。フードの下でAngularが何かをしない限り(終了時に他のコールバックを無効にするなど)、ここでメモリリークが発生する可能性があります。 –

答えて

4

$http()コール(またはhugeDataにアクセスできるオブジェクトまたは機能)の結果をfetchResultsの外部スコープに戻すとすぐにメモリリークが発生します。

コードでは、fetchResultsの外部に直接大きなものは公開されず、成功または失敗するまで$http()呼び出しの結果が得られ、対応するコールバックを呼び出して最終的にGC'edになります。

は見識を参照してください:@ŁukaszBachmanが観察http://jibbering.com/faq/notes/closures/#clIdRes

ように、これは何のメモリリークがないことを保証するものではありません。あなたの大きなオブジェクトやスコープ内の大きなオブジェクトを持つあなたのコールバックへのどんなダングリング参照も、悲惨さを引き起こします。

したがって、$qの実装($http$qに基づいています)を確認してみましょう。

あなたがhttps://github.com/angular/angular.js/blob/master/src/ng/q.js#L191をチェックすると、あなたが見ることができる繰延最初のコピーのresolve()方法方法にローカル変数における登録のコールバックのリスト:

var callbacks = pending; 

は、その後、外部pendingは(それが定義された無効化

pending = undefined; 

deferレベルで、その後、次のティックで、コールバックを実行します。コールバックの引数が遅延自体(実行にさらに遅延を加える)かもしれないという事実が複雑になるかもしれませんが、せいぜいあなたは無限ループに陥る可能性があります。 (それは面白くない!)。ループに入ることができないほど幸いなら、ある時点でコールバック配列が使い尽くされ、コールバックリストへの参照が何もないので、GCで利用できるようになります。

しかし、

強制すると状況が悪くなる可能性があります。

コールバック内でarguments.calleeを使用できます。

キーボードでもビールを投げることができます。

あなたが窓から飛び降りると、1階に住んでいなければ、おそらく怪我をするでしょう。

ハッピーEcmaScripting!

+0

$ http()呼び出しの結果は、成功するか失敗するまで生き残るのですか? AFAIU閉鎖は私のJSエンジン自体で管理されているので、私の例ではあなたは確信が持てませんよね? '$ http'の内部implをチェックして、2番目のコールバックが破棄され、GCによって解放されることを確認する必要があります。 私は正しいですか? (提供された記事を読んだリストに追加しました) –

+0

'$ q'実装、特に' resolve'メソッド(https://github.com/angular/angular.js/blob/master/src/ng/)をチェックしてくださいq.js#L191)。 'pending'コールバックチェーンは、約束が解決されるとすぐに約束のスコープから完全に削除され、resolveメソッドの別の変数に内部化されます。解決が完了すると、コールバックへの参照は残っていないので、コールバックをGCで使用できるようになります。 – rewritten

+0

つまり、あなたは正しいです。そしてそれはまさにそのように行われ、登録されたコールバックの完全なリストは解決プロセスでクリーンアップされます。 – rewritten