2013-10-23 33 views
48

JavaScriptを使用してクロージャを使用すると、メモリリークが発生することがよくあります。ほとんどの場合、これらの記事は、スクリプトコードとDOMイベントを混在させることを指しています。スクリプトはDOMを指し、逆もまた同様です。JavaScriptのメモリリークとクロージャ - その理由とその理由

私はクロージャがそこで問題になる可能性があることを理解します。

しかし、Node.jsはどうですか?ここでは、当然DOMを持っていないので、ブラウザのようにメモリに副作用が漏れることはありません。

閉鎖には他にどのような問題がありますか?誰かがこれについての詳細なチュートリアルを手がけてくれますか?

この質問は、ブラウザではなくNode.jsを明示的に対象としています。

+0

私はあなたが探しているものを理解していますか?オブジェクトの管理方法について心配する必要があり、クロージャによってオブジェクトが保持されていることが分かりにくくなる可能性がありますが、コードではオブジェクトが多すぎてメモリが不足する可能性があります。 – WiredPrairie

+0

はい、もちろんです。しかし、ベストプラクティスがありますか、またはメモリリークを避けるために注意すべきパターンがありますか? Node.jsの非同期性のため、私はクロージャを使わないと想像するのは非常に難しいです。だから、私が*使用する*場合は、どのようなガイドラインに従う必要がありますか?または、言い換えれば、どこで問題が発生するのでしょうか? –

+3

私が提供できるベストガイダンス:「閉鎖を理解する」閉鎖を理解すれば、それは彼らの謎を取り除きます、そしてあなたは彼らの使用のための特別なガイドラインを必要とすべきではありません。 – WiredPrairie

答えて

34

This questionについて尋ねます。基本的には、コールバックでクロージャを使用する場合は、コールバックを終了したときにコールバックを解除して、GCが再度呼び出すことができないことを知る必要があります。これは私には意味があります。クロージャーが呼び出されるのを待っているだけの場合、GCは終了したことを知るのに苦労します。コールバックメカニズムからクロージャを手動で削除することにより、クロージャは参照されずに収集されます。

また、Mozillaはa great article on finding memory leaks in Node.jsコードを公開しています。私は、あなたの戦略のいくつかを試してみると、漏れのある行動を表すコードの部分を見つけることができると思います。ベストプラクティスは素晴らしく、すべてですが、私はプログラムのニーズを理解し、経験的に観察できるものに基づいていくつかのパーソナライズされたベストプラクティスを考え出す方がより効果的だと思います。 GCC mtraceユーティリティを使用しています

  • Jimb Esser氏のnode-mtraceは、ヒープ使用量をプロファイルする:

    はここで、Mozillaの記事からの迅速な抜粋です。

  • Dave Pachecoのnode-heap-dumpは、V8ヒープのスナップショットを取り、巨大なJSONファイルですべてを直列化します。結果スナップショットをトラバースしてJavaScriptで調査するツールが含まれています。
  • Danny Coatesのv8-profilerおよびnode-inspectorは、WebKit Webインスペクタを使用して、V8プロファイラおよびノー​​ドデバッグインターフェイスのノードバインディングを提供します。
  • リテーナーが
  • フェリックスGeisendörferのノードのメモリリークのチュートリアルがv8-profilernode-debuggerを使用する方法の短いと甘いの説明で、現在最先端のザ・あるをグラフ非無効のと同じのフェリックスGnassのフォークほとんどのNode.jsメモリリークのデバッグのための技術です。 Node.jsのメモリをデバッグするためにあなたの処分でツールの兵器庫を供給する
  • ジョエントのSmartOSプラットフォームは、

をリークthis questionへの回答は基本的にあなたがnullを割り当てることにより、GCを助けることができると言います変数を閉じる。

var closureVar = {}; 
doWork(function callback() { 
    var data = closureVar.usefulData; 
    // Do a bunch of work 
    closureVar = null; 
}); 

すべての変数は、関数が消えます内を宣言した関数が戻るとき、他の閉鎖に使用されているものを除き、。この例では、callback()が呼び出されるまでclosureVarをメモリに格納する必要がありますが、そのときはいつ知っていますか?コールバックが呼び出されると、クロージャ変数をnullに設定してGCにヒントを与えることができます。

免責事項:以下のコメントからわかるように、この情報はNode.jsには致命的ではないと言っているSOユーザーがいます。私はまだそれに決定的な答えはありません。私はちょうど私がウェブ上で見つけたものを投稿しています。

+1

私のコメントを参照してくださいhttp://stackoverflow.com/q/5733665/557481 – plalx

+0

私はplalxに同意する、nullを設定する何もしません。これは、古いガベージコレクションのためにIEの古いブラウザで必要だったトリックでした。 V8やMozilla Seamonkeyに問題が起きるという証拠はほとんどありません。あなたがリンクしている質問も2歳ですが、私はそれがここでは関係がないと思っています。同じ質問で、delnanのコメントは、V8では循環閉鎖が問題ではないと述べています。 – user568109

+1

私はあまりにも質問を読んで、クローズがコレクションにとって安全でないと答えることを考えました。しかし、ネット上でこれを検証すると、そのようなすべての記事がDOMに関連していることがわかりました。ブラウザーに関連したGCの問題(IE?)を暗示しています。あまりにも数年前。私はクロージャーでこのような模様がV8 GCによって収集されなかったという証拠を見つけることができませんでした。さらに、この技術がV8で動作することを証明するベンチマークはありません。 – user568109

2

私は閉鎖がメモリリークの原因であることに同意しなければなりません。これは、古いバージョンのIEではあまりにも厄介なガベージコレクションのために当てはまります。 Douglas Crockfordのthisの記事を読んでください。これはメモリリークが何であるかを明確に述べています。

再生されていないメモリがリークしていると言われています。

リークは問題ではなく、効率的なガベージコレクションです。リークは、ブラウザとサーバーの両方のJavaScriptアプリケーションで発生する可能性があります。 V8を例に取る。ブラウザでは、別のウィンドウ/タブに切り替えると、タブでガベージコレクションが行われます。アイドリング時にリークが塞がれます。タブはアイドル状態になることがあります。

サーバー上での操作は簡単ではありません。漏れが発生する可能性がありますが、GCはコスト効率に優れていません。サーバはGCを頻繁に使用する余裕がないか、その性能に影響を与えます。ノードプロセスが特定のメモリ使用量に達すると、ノードプロセスはGCで起動します。リークは定期的に取り除かれます。しかし、リークは依然として速いスピードで起こり、プログラムがクラッシュする可能性があります。

+0

建設的な批判は大歓迎です。ダウンボートは、コメントを追加してください。 – user568109

+0

-1閉鎖はメモリーリークの原因にはならないので、読者の中には答えがあるかもしれないと思います。 https:// github:クロージャでは、メモリリークが非常にうまく隠れることがあります。com/jedp/node-memwatch-demo#some-examples-of-leaks – borisdiakur

+0

@Lego私はこれを十分に強調することはできません。答えを完全に読んでください。あなたは最初の行を読んでいるようです。手元にあることを十分に理解していない場合は、ダウンワードを控えてください。あなたのポイントは、閉鎖のためにメモリリークが発生する可能性があることです。私はそれを否定しない。私のV8は、閉鎖によって発生するリークを処理できるかなり良いガベージコレクションを持っています。あなたのリンク自体が私の主張を証明します。ガベージコレクションは、時間の経過とともに(非同期的に)ゆっくりとペースが速くなったり、ペースが速く(プラグに漏れが多い)、通行料がかかります。 – user568109

9

this blog postには、David Glasserの良い例と説明があります。

さて、ここでは(私はいくつかのコメントを追加しました)です:

var theThing = null; 
var cnt = 0; // helps us to differentiate the leaked objects in the debugger 
var replaceThing = function() { 
    var originalThing = theThing; 
    var unused = function() { 
     if (originalThing) // originalThing is used in the closure and hence ends up in the lexical environment shared by all closures in that scope 
      console.log("hi"); 
     }; 
     // originalThing = null; // <- nulling originalThing here tells V8 gc to collect it 
     theThing = { 
      longStr: (++cnt) + '_' + (new Array(1000000).join('*')), 
      someMethod: function() { // if not nulled, original thing is now attached to someMethod -> <function scope> -> Closure 
       console.log(someMessage); 
      } 
     }; 
    }; 
setInterval(replaceThing, 1000); 

でそれを試してみるとChromeのDevツールでoriginalThingをゼロにすることなく、(タイムライン]タブ、メモリビュー、レコードをクリック)してください。上記の例は、ブラウザおよびNode.js環境に適用されることに注意してください。

クレジットとも特にVyacheslav Egorovです。

+0

上記の他の例を追加できますか?また、このスニペットがリークすることが予想され、余分な行をヌルに設定してプラグすることもできます。 – user568109

+0

@borisdiakurそのスニペットをお寄せいただきありがとうございます。長い批評の夜の後、最初の本当に有益な投稿でした!しかしもう一度質問してみましょう...この行動を扱う適切な方法は何ですか?共有されていないコールバックで各変数nullを設定していますか?実際には--expose-gcでノードを実行し、global.gc()を定期的に呼び出すと、メモリを安定した状態に保つことができます(100Mbと連続して300Mbを使用しない場合) – Bernhard

+0

@Bernhardは '--expose-gc'と'gc()'を定期的に呼び出すのは悪い習慣です。 V8は[インクリメンタルマーキングと遅延スィーピング](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection)を介して必要に応じてゴミを収集します。上記の動作を処理する適切な方法は、レキシカル環境の一部となっているオブジェクトを指すローカル変数をnullにすることです。 – borisdiakur

関連する問題