web-dev-qa-db-ja.com

Node.js / V8で非常に長いGC一時停止をデバッグ/分析する方法

比較的複雑なNode.jsサーバーアプリでメモリ/ GCの問題を分析しようとしています。非常に中程度の負荷の下でも、目立った期間は応答しなくなり、これらの一時停止は時間の経過とともに長くなります。 --trace-gcパラメーターを指定して実行すると、ガベージコレクション時間が非常に長いことが原因である可能性が高いことがわかります。

[4805]      537 ms: Mark-sweep 17.6 (46.4) -> 10.3 (47.4) MB, 20 ms [allocation failure] [GC in old space requested].
[4805]     1338 ms: Mark-sweep 31.3 (58.4) -> 19.2 (57.2) MB, 40 ms [allocation failure] [promotion limit reached].
[4805]     2662 ms: Mark-sweep 58.0 (79.2) -> 43.9 (85.2) MB, 109 ms [Runtime::PerformGC] [promotion limit reached].
[4805]     4014 ms: Mark-sweep 90.1 (111.5) -> 70.6 (113.9) MB, 114 ms [allocation failure] [promotion limit reached].
[4805]     7283 ms: Mark-sweep 129.7 (153.9) -> 112.0 (158.9) MB, 511 ms [allocation failure] [promotion limit reached].
[4805]    10979 ms: Mark-sweep 184.6 (210.9) -> 160.3 (212.9) MB, 422 ms [Runtime::PerformGC] [promotion limit reached].
[4805]  1146869 ms: Mark-sweep 243.8 (271.4) -> 191.6 (267.9) MB, 1856 ms [allocation failure] [promotion limit reached].
[4805]  1731440 ms: Mark-sweep 282.1 (307.4) -> 197.5 (298.9) MB, 1 / 11230 ms [allocation failure] [promotion limit reached].
[4805]  2024385 ms: Mark-sweep 291.0 (320.8) -> 197.3 (306.9) MB, 9076 ms [Runtime::PerformGC] [promotion limit reached].
[4805]  2623396 ms: Mark-sweep 290.9 (317.1) -> 196.9 (311.9) MB, 1 / 15401 ms [allocation failure] [promotion limit reached].
[4805]  3223769 ms: Mark-sweep 291.4 (323.6) -> 187.8 (318.9) MB, 1 / 13385 ms [allocation failure] [promotion limit reached].
[4805]  4225777 ms: Mark-sweep 280.1 (324.2) -> 190.6 (315.9) MB, 1 / 13266 ms [allocation failure] [promotion limit reached].
[4805]  4705442 ms: Mark-sweep 286.2 (321.4) -> 195.2 (314.9) MB, 1 / 17256 ms [Runtime::PerformGC] [promotion limit reached].
[4805]  5225595 ms: Mark-sweep 288.3 (324.0) -> 201.7 (316.9) MB, 1 / 22266 ms [Runtime::PerformGC] [promotion limit reached].
[4805]  6127372 ms: Mark-sweep 296.5 (324.6) -> 200.5 (316.9) MB, 1 / 28325 ms [allocation failure] [promotion limit reached].
[4805]  6523938 ms: Mark-sweep 297.8 (328.9) -> 198.8 (323.9) MB, 1 / 27213 ms [allocation failure] [promotion limit reached].
[4805]  7355394 ms: Mark-sweep 292.1 (330.7) -> 223.9 (322.9) MB, 60202 ms [allocation failure] [promotion limit reached].

完全な(--trace-gc-verbose)出力は ここ にあります。

これらのログは、次のパラメーターを使用してサーバーを実行した結果です。

--expose-gc --trace-gc --trace-gc-verbose --trace-gc-ignore-scavenger --max-old-space-size=1000

実行時間が長くなるほど、一時停止が長くなり(多くの場合、数分)、数時間後に完全にロックされます。使用可能なメモリが不足することはなく、RSSは1000 MBの古いスペース制限にさえ近づかないため、リークのようには見えません。コード内にかなり異常なことがあるように思われるため、GCが許容可能な時間枠でその作業を実行することは非常に「困難」です。

私の質問は、この問題をさらに分析し、考えられる原因を絞り込むにはどうすればよいですか?このような問題に役立つ推奨ツールはありますか?私は基本的に、コードの一部を単純にオフにしたりオンにしたりするよりも効率的なアプローチを探しています。これは非常に面倒で時間がかかります。

余談ですが、GCデバッグ出力で使用される用語/メッセージ(「プロモーション制限に達しました」など)とそこにリストされている番号を説明するドキュメントへのリンクをいただければ幸いです。私はV8GCがどのように機能するかについて非常に基本的な理解を持っています( this 大いに役立ちました)が、その出力のほとんどはまだ私を超えています。

重要な場合:これは、Ubuntu14.04サーバー上のNode.jsv0.10.33で実行されています。

編集:しばらく前にio.jsに移動しましたが、この問題は発生しなくなりました(おそらく、はるかに新しいV8バージョンが原因です)。 。 Node v0.10でこの問題の原因を見つけることはできませんでしたが、修正は言うまでもありません。

22
d0gb3r7

単一のノードで問題を再現できますか?私がその状況にあったら、私はおそらく次のことを組み合わせて行うだろうと思います:

  • ローカルインスタンスで複製できるローダーを作成する
  • そうでない場合は、トラフィックのサブセットを受信するインスタンスをprodに配置し、以下を実行するように変更します
  • node-heapdump をソースに追加し、一定の間隔で呼び出し、結果をN分間隔でjsonファイルにエクスポートします。
  • ローカルで実行している場合は、 memwatch を利用できる可能性もあります。
  • 遅いGCが開始するのを待ちます。
  • 遅いGCが発生し始めたことがわかっている頃に、いくつかのヒープダンプを取得します。
  • それらをchromeにロードし、 スナップショット手法 を使用して分析します(この場合、これをNスナップショット手法と呼ぶことができると思います)

基本的に、ヒープをロードし、それらを調べて、どのタイプのものが積み重なっているか、何がそれを保持しているかを理解しようとします。その結果、GCに非常に時間がかかる理由を理解できます。

使用可能なメモリが不足することはなく、RSSは1000 MBの古いスペース制限にさえ近づかないため、リークのようには見えません。コード内にかなり変わったものがあるように思われるため、GCが許容可能な時間枠でその仕事をすることは非常に「困難」です。

ここでは、長くて円形の保持木を探しているかもしれません。しかし、結局のところ、その場合でも、そのツリーのルートが何であるか、その中に何があるかを識別し、削除を減らす方法を試すことができるはずです。

@dandavisと疑わしい閉鎖にも同意します。

5
j03m

この答えはあなたが望むほど具体的ではないかもしれませんが、ウォルマートの hapi.js フレームワークの一部である 良いパッケージ を見ることをお勧めします。ロギングを_--trace-gc_を超えて拡張するという素晴らしい仕事をします。これは、次の1つ以上のイベントをリッスンするプロセスモニターです。

  • ops-システムとプロセスのパフォーマンス-CPU、メモリ、ディスク、およびその他のメトリック。
  • response-着信要求と応答に関する情報。これは、hapiサーバーから発行された「応答」または「テール」イベントのいずれかにマップされます。
  • log-システムエラー、バックグラウンド処理、構成エラーなど、特定のリクエストにバインドされていないログ情報。hapiサーバーから発行された「log」イベントにマップされます。
  • error-ステータスコードが500の応答を要求します。これは「request-error」hapiイベントにマップされます。
  • request-ログ情報を要求します。これは、request.log()を介して発行されるhapi'request 'イベントにマップされます。

これを機能させるには、Hapiライブラリをプルする必要がありますが、デバッグの目的で一時的に価値がある場合があります。一般的に、Node.jsアプリのスケーリングにはHapiを強くお勧めします。ウォルマートのスタッフは、過去1年間Hapiを使って素晴らしいことを行ってきました。

0
Kevin Leary