2012-01-17 4 views
10

私は新しい開発者のインタビューを手伝ってくれました.JavaScriptは私の役割と役割の大部分ですのための募集。正直言って、候補者はそれほど良くはなかったし、彼はJavaScriptを本当に理解していなかったが、インタビューではJavaScriptをC#と混同し、JSでメモリリークについて話し始めた。しかし、私が介入したかったのは、JSのメモリリークについて知っていたのは、メモリを大量に使い果たしてしまったことを除けば、どれほどのリークがあるのか​​分かりませんでした。JavaScriptでメモリリークが発生しました:それらを見分ける方法、作成方法

私が覚えることができる唯一のことは、マークと掃引のガベージコレクションについて言及しているOReillyのDef Guide(第4版だと思います)です。しかし、それは私がそれを読んで以来、衰えてきており、私はそれを本当に拡張することはできません。私は、この問題については、明らかではなく、簡潔であることが分かっています。

JSのメモリリークは何ですか?どのようにそれらを見つけ出すことができますか、それらを作成する方法 - 私はJSを何年も書いてきましたが、これは私の知識と信頼を完全に失いました。私はそれについて本当に考えたことはありません!

答えて

4

実は、「真」のメモリリークが自動ガベージコレクタを持つ言語で可能になることはありません。だからメモリリークがある場合、その下層エンジンには常にエラーがあります(たとえば、一部のIEではnamed function expressionsの問題)。

私たちはそれを解明した後も、JavaScriptを使用して多くのメモリを取得し、解放せずに保持することは可能です。とにかくそれは本当のメモリリークではありません。たとえば、各function callは、ECMAscriptでクロージャを作成します。レキシカルクロージャは、とりわけ、各親コンテキストのデータ(アクティベーションオブジェクトとバリアブルオブジェクト)への参照をコピーします。だから、これはいくつかのメモリを必要とします。特に、たくさんのクロージャを作成している場合はそうです。

Javascript DOMの世界の別の例:new Image()を使用して動的イメージを作成し、ソースを大きなイメージに設定しています。さて、画像への参照があり、すべての参照がなくなるまでガベージコレクションを行うことはできません(良いメモリツールで、メモリが画像用で、JavaScript用ではないことが正しく伝えられたとしても)。

実際には、実際にはこの言語でメモリを「リーク」できる唯一のシナリオです。再び、それは実際にはfree()そのセクションを忘れるC malloc()のようなメモリをリークしていません。 ECMAscriptにはダイナミックメモリ管理がないので、このことは全くあなたの範囲外です。

+0

よくメモリーリークです。これは、「無料で電話するのを忘れるのはメモリリークではなく、解放せずに持ちこたえている」ということです。 – Raynos

+0

@レイノス:違う話はありません。 – jAndy

+0

[どう違うのですか](https://gist.github.com/1627097) – Raynos

1

Javascriptは、すべてのブラウザで異なるように実装されています。しかし、すべてのブラウザに従うべき標準があります:ECMAscript

現代のすべての言語がreference countingという独自のバージョンを実装していることを考慮して、メモリリークを回避する最良の方法は、未使用の変数をすべてnullに参照していることです。

3
var trolls = (function() { 
    var reallyBigObject = eatMemory(); 

    // make closure (#1) 
    // store reallyBigObject in closure 
    (function() { 
    var lulz = reallyBigObject; 
    })(); 

    // make another closure (#2) 
    return function() { 
    return 42; 
    }; 
})(); 

あなたはtrollsがちょうどfunction() { return 42; }ことを期待するとあなたはreallyBigObjectが除去され、ガベージコレクト期待します。

単一のクロージャ(#1)が外側スコープ内の変数を参照する場合は、そうではありません。その後、すべてのクロージャ(#2も同様)がその変数を参照します。

#2への参照があるということは、reallyBigObjectへの参照があり、#2が死んでしまうまでクリアされないことを意味します。

ここでは、クロージャーですべてをラップし、10個の深みを入れ子にする平均的なクロージャーの重いアーキテクチャーを考えてみましょう。オブジェクトへの参照を保持するのが簡単に分かります。

上記の詳細はv8に適用されます。すべての内部関数はES5通り、外側のスコープで定義されたすべてのクロージャ変数への参照を持っている必要があるため、任意の完全ES5対応ブラウザは

var trolls = (function() { 
    var reallyBigObject = eatMemory(); 
    return function() {}; 
})(); 

と漏れることになります。ほとんどのブラウザは、ショートカットを使用して、目立っていない方法でこれを最適化します。

+0

私は現代のGCはそれほど愚かではないと思います。私を信じて、現在のGCはこの種のものを非常にうまく分析します。とにかくそれについて@gsneddersにインタビューすべきです。 – jAndy

+1

@jAndy彼らはこのばかだ、これはまさにv8の仕組みだ。すべての内部関数は、同じクロージャコンテキストオブジェクトを共有します。そのクロージャコンテキストオブジェクトには、関数のすべてのクロージャが必要とするすべての変数の和集合が含まれています。 ES5仕様では、内部関数はすべて、クロージャで使用可能なすべての変数への参照を格納する必要があるとしています。 – Raynos

関連する問題