なぜパフォーマンス監視が必要なのでしょうか?この記事では、Node.js のパフォーマンス監視について説明します。お役に立てば幸いです。
パフォーマンス監視が必要な理由
ノードサーバー上の Javascript のランタイム (Runtime) として側面としては、JavaScript のアプリケーション シナリオが大幅に強化されます。
しかし、Node.js ランタイム自体はブラック ボックスであり、私たちは ランタイムの状態を 認識することができず、 オンラインの問題を再現することは 困難です。
したがって、パフォーマンス監視は、Node.js アプリケーションの「通常の操作」の基礎となります。さまざまな実行時インジケーターをいつでも監視できるだけでなく、異常なシナリオの問題のトラブルシューティングにも役立ちます。
コンポーネント
パフォーマンス監視は 2 つの部分に分けることができます:
-
パフォーマンス指標の収集と表示
- プロセス レベルのデータ: CPU、メモリ、ヒープ、GC など。
#システム レベルのデータ: ディスク占有率、I/O 負荷、TCP/UDP 接続ステータスなど。-
アプリケーション層データ: QPS、低速 HTTP、ビジネス処理リンク ログなど -
##パフォーマンス データのキャプチャと分析-
ヒープスナップショット: ヒープ メモリ スナップショット
- Cpuprofile: CPU スナップショット
- コアダンプ: アプリケーション クラッシュ スナップショット
-
スキーム比較
上の図から、現在主流の 3 つの Node.js パフォーマンス監視ソリューションの長所と短所がわかります。これら 3 つのソリューションの構成を簡単に紹介します。
Prometheus-
prom-client- は、パフォーマンス指標を収集するために使用される prometheus の Nodejs 実装です
grafana- は、さまざまなデータ チャートを表示し、prometheus へのアクセスをサポートするために使用される視覚化プラットフォームです。パフォーマンス インジケーターの収集と表示のみをサポートします。閉ループを形成する問題のトラブルシューティングには、他のスナップショット ツールが必要です
-
AliNode-
alinode は、公式の Nodejs と互換性のある拡張ランタイムであり、いくつかの追加機能を提供します。 -
v8 実行時メモリ状態監視
##libuv の実行時状態監視- ##オンライン障害診断機能: ヒープスナップショット、CPU プロファイル、GC トレースなど
-
- agenthub
は、パフォーマンス指標を収集してレポートするために使用される常駐プロセスです。
- ##agentx
commdx を統合する便利なツールです。
- Easy-Monitor
xprofiler- リアルタイムの実行時ステータスのサンプリングとパフォーマンス ログの出力 (つまり、パフォーマンス データの取得)
xtransit
パフォーマンス ログの収集と送信を担当します
- AliNode との最大の違いは、## を使用することです。 #Node.js アドオン 実装用サンプラー
パフォーマンス インジケーター
CPU
##process.cpuUsage() により、現在のプロセスの CPU 消費量データを取得できます。戻り値の単位はマイクロ秒です。
user: 実行中にプロセス自体によって消費される CPU Time
system: プロセスの実行時にシステムによって消費される CPU 時間
Memory
- Pass
process.memoryUsage()- 現在のプロセスのメモリ割り当てデータを取得できます。戻り値の単位はバイトです
rss: 常駐メモリ、ノード プロセスによって割り当てられた合計メモリ サイズheapTotal: v8 によって適用されたヒープ メモリ サイズ
heap Used: v8 によって使用されたヒープ メモリ サイズ
external: v8 によって管理される C が占有するメモリ サイズ
arrayBuffers: ArrayBuffer に割り当てられたメモリ サイズ
-
上の図からわかるように、rss
にはコード セグメント (コード セグメント
)、スタック メモリ (Stack
)、およびヒープ メモリ (ヒープ
)
- コードセグメント: コードセグメントを保存します
- スタック: ローカル変数と管理関数呼び出しを保存します
- ヒープ: オブジェクト、クロージャ、またはその他 すべて
#Heap
は、v8.getHeapStatistics()
および v8.getHeapSpaceStatistics(() を通じて v8 から取得できます。 )
ヒープ メモリとヒープ スペースの分析データ。次の図は、v8 のヒープ メモリ構成の分布を示しています。
ヒープ メモリ スペースは、まずスペースに分割されます。 、スペースはページに分割され、メモリは 1MB の位置合わせに従ってページングされます。
-
新しいスペース: 新しい世代のスペース。ライフサイクルが比較的短いオブジェクト データを保存するために使用され、2 つのスペースに分割されます (スペース タイプは セミ スペース
)。 from space
、to space
- 昇格条件: 新しいスペースで 2 回の GC 後も存続
##Old Space: 古い世代のスペース、New Space
プロモートされたオブジェクトの格納に使用されます
Code Space: v8 JIT コンパイルされた実行可能コードの格納
-
マップ スペース: Object が指す隠しクラスのポインタ オブジェクトを格納します。隠しクラス ポインタは、実行時に v8 によって記録されるオブジェクト レイアウト構造であり、オブジェクト メンバーにすばやくアクセスするために使用されます
- #ラージ オブジェクト スペース: ページに割り当てられない 1MB を超えるオブジェクトを保存するために使用されます
GC
v8 ガベージ コレクションアルゴリズムは 2 つのカテゴリに分類されます。
メジャー GC: 旧世代でのオブジェクトのリサイクルに - Mark-スイープ-コンパクト
アルゴリズムを使用します。
マイナー GC: を使用します。新世代のオブジェクトリサイクルのための - Scavenge
アルゴリズム
Scavenge
前提: New space は 2 つのオブジェクト スペースに分割されます:
from と
to
トリガー タイミング: New space space Full# のとき
##ステップ:
マーク- スイープ - コンパクト
3 つのステップ: マーク、クリア、整理
トリガー時間: 古いスペース
がいっぱいになったとき手順:
マーキング (3 色マーキング方法)
白: リサイクル可能なオブジェクトを表します-
黒 :リサイクル不可能なオブジェクトを表し、それによって生成された参照はすべてスキャンされています。
グレー: リサイクル不可能なオブジェクトを表し、それによって生成された参照はスキャンされていません。- 直接配置V8 ルート オブジェクト 参照されたオブジェクトは
- マーキング キュー
(明示的なスタック) に置かれ、これらのオブジェクトは灰色でマークされます- これらのオブジェクトから開始して、深さ優先のトラバーサルを実行します。オブジェクトがアクセスされると、そのオブジェクトは
- マーキング キュー
pop から出てきて黒とマークされます -
その後、オブジェクト参照の下にあるすべての白いオブジェクトが灰色でマークされます##push
マーキング キュー
に移動し、スタック上のすべてのオブジェクトがポップされるまで
- を繰り返します。旧世代には 2 種類のオブジェクトしかありません: 黒 (非
PS: オブジェクトが大きすぎてスペースが限られているスタックにプッシュできない場合、v8 はオブジェクトを灰色のままにしてスキップし、スタック全体を次のようにマークします。スタックがクリアされた後、再度プッシュされ、マークをトラバースします。これには、ヒープの追加スキャンが必要になります。 #白いオブジェクトを消去すると、メモリ容量が不足します。 Continuous
- Compact
-
- スイープによりメモリ空間が不連続になるため、新しいオブジェクトが GC に入るのは好ましくありません。
- 黒い (生存) オブジェクトを
Old オブジェクトの一端に移動します。スペース
、空にできるようにします スペースは連続した完全なスペースです #メモリの断片化の問題は解決できますが、一時停止時間が長くなります (実行速度が遅くなります) - これは、新しい世代からプロモートされたオブジェクトを割り当てるための十分なスペースがない場合にのみ使用されます。
- v8 が最初にガベージ コレクションを実行するときは、プログラムの実行を停止する必要があります。ヒープ全体がスキャンされてメモリが再利用されるまで、プログラムは再実行されません。この動作はフルストップと呼ばれます (
Stop-The-World
)
新世代のアクティブ オブジェクトは小さくなり、頻繁にリサイクルされるため、フルストップの影響はほとんどありませんが、多くのオブジェクトが生き残っています。そして、それが大きい場合、マーキング、クリーニング、分類などによって生じる一時停止はより深刻になります。
最適化戦略
増分マーキング: マーキング フェーズでは、ヒープが特定のサイズに達すると、増分 GC が開始され、一定量のメモリが割り当てられた後、各割り当てが開始されます。メモリが確保され、プログラムが一時停止され、マーキングが数ミリ秒から数十ミリ秒行われてから、プログラムが再開されます。
この概念は、実際には、React フレームワークのファイバー アーキテクチャに少し似ています。ブラウザーの空き時間中にのみ、対応するタスクを実行するためにファイバー ツリーをトラバースします。それ以外の場合、実行は遅延します。メインスレッドのタスクへの影響を最小限に抑え、アプリケーションの遅延を回避し、アプリケーションのパフォーマンスを向上させます。
同時スイープ (並行スイープ): 実行中のプログラムのメインスレッドとの競合を気にせずに、他のスレッドに同時にスイープを実行させます。
並列スイープ (並列スイープ):複数のスイープ スレッドを同時に動作させることで、スイープのスループットが向上し、GC サイクル全体が短縮されます-
スペース調整
- デフォルトのスペース制限のため新世代と旧世代の v8 サイズは
-
#新しいスペースです。デフォルトの制限: 64 ビット システムの場合は 32M、32 ビット システムの場合は 16M
Old space デフォルトの制限: 64 ビット システムは 1400M、32 ビット システムは 700M
- したがって、
node
には調整用の 2 つのパラメーターが用意されています。新世代と旧世代のスペースの上限
-
--max-semi-space-size :
New Space の最大値を設定します
--max-old-space-size: - Old Space の最大値を設定します
GC ログの表示-
node
も提供されています。 GC ログを表示する 3 つの方法:
--trace_gc: 1 行のログの簡潔な説明各 GC の時間、タイプ、ヒープ サイズの変更と原因を説明します
--trace_gc_verbose
: 各 GC 後の各 V8 ヒープ スペースの詳細なステータスを表示します
#--trace_gc_nvp- : GC タイプ、一時停止時間、メモリ変更などを含む、各 GC の詳細なキーと値のペア情報。
GC ログは比較的原始的なため、二次処理が必要です。 、AliNode チームによって開発された - v8-gc-log-parser## を使用できます。
#スナップショット ツール
Heapsnapshot
実行中のプログラムの ヒープ メモリ
のスナップショット サンプリングを実行します。メモリ消費と変更の分析に使用できます。
生成方法
.heapsnapshot
ファイルを生成するにはいくつかの方法があります:
heapdump
# を使用します。
- nodejs 組み込み v8 モジュールによって提供される API を使用します
#v8.getHeapSnapshot()
-
##v8.writeHeapSnapshot(fileName)
##使用
v8-profiler-next
分析方法
生成された - .heapsnapshot
ファイルを Chrome devtools ツールバーのメモリにアップロードして選択すると、表示結果は次のようになります:
デフォルトのビューは、サマリー
ビューです。ここでは、一番右の 2 つの列、Shallow Size
と Retained Size
## に注目します。
- #Shallow Size
: v8 ヒープ メモリに割り当てられたオブジェクト自体のサイズを示します。
- Retained Size
: すべてのオブジェクトの
Shallow Size# を示しますオブジェクト ##sum
の参照オブジェクト
Retained Size が特に大きいことが判明した場合、オブジェクト内でメモリ リークが発生している可能性があり、さらに拡張して特定することができます。問題
と
比較ビューは、2 つの異なる期間のヒープ スナップショットを比較および分析するために使用されます。Delta
列を使用して、オブジェクトをフィルターで除外できます。最大のメモリ変更
Cpuprofile
#実行中のプログラムの
CPU のスナップショット サンプリングを使用して、 CPU 時間の消費量と割合を分析します。
生成方法
.cpuprofile ファイルを生成するには、いくつかの方法があります。
v8-profiler- (ノード ツールによって正式に提供されますが、ノード v10 以降はサポートされなくなり、メンテナンスも終了しました)
v8-profiler-next- (中国のメンテナンス バージョン、最新のノード v18 をサポート、継続的なメンテナンス中)
これは 5 分間収集された CPU プロファイル サンプルです
分析メソッド
生成された
.cpuprofile ファイルは、Chrome devtools ツールバーの Javascript Profiler
に表示できます (デフォルトのタブではなく、必要に応じて)ツールバーの右側にある [その他] で開きます)。アップロード ファイルを選択すると、結果が表示されます。以下に示すように:
デフォルトのビューは
Heavy ビュー。ここには、Self Time
と Total Time
Self Time- の 2 つの列が表示されます。この関数自体の実行時間を表します (他の呼び出しを除く)
#Total Time - : この関数の合計実行時間を表します (他の呼び出し関数を含む)
Total Time と Self Time
の間の偏差が大きいことが判明した場合、この関数は時間がかかり、CPU を集中的に使用する計算になる可能性があります。さらにトラブルシューティングを実行することもできます
コードダンプ
アプリケーションが予期せずクラッシュして終了すると、システムはプロセスがクラッシュした瞬間のメモリ割り当て情報、プログラム カウンター、スタック ポインター、その他の重要な情報を自動的に記録します。コア ファイルの生成
生成メソッド
Generate
.core ファイルの 3 つのメソッド:
ulimit -c unlimited- オープンカーネル制限
node --abort-on-uncaught-Exception - ノードの起動時にこのパラメータを追加すると、キャッチされない例外が発生したときにコアファイルを生成できますアプリケーション内
#gcore - コア ファイルを手動で生成
解析方法
取得後
.core ファイルを使用すると、mdb、gdb、lldb、およびその他のツールを使用して、実際のプロセス クラッシュの原因を分析および診断できます
llnode `どのノード` -c /path/to/core/dump
ケース分析
観察
監視すると、ヒープ メモリが増加し続けていることがわかります。そのため、トラブルシューティングにはヒープ スナップショットが必要です。
##分析
#heapsnapshot によると、分析すると、常に比較的大きなメモリを維持する
newThing オブジェクトが存在することがわかります
トラブルシューティング
コードからわかるように、unused メソッドは呼び出されませんが、
newThingオブジェクトは theThing
から参照されているため、常に replaceThing
の実行コンテキストに存在し、解放されていません。これは、クロージャ # によって引き起こされるメモリ リークの典型的なケースです。
##概要
一般的なメモリ リークには次の状況が含まれます。
- グローバル変数
- クロージャ
- タイマー
- イベントリスニング
- キャッシュ
So in上記の状況では、オブジェクトがメモリ内で自動的にリサイクルされるかどうかを慎重に検討する必要があります。自動的にリサイクルされない場合は、オブジェクトを手動で null
に設定し、
#概要
これまで、この記事では Node.js パフォーマンス監視システム全体について詳しく紹介してきました。 まず、パフォーマンス監視によって解決される問題、そのコンポーネント、および主流のソリューションの長所と短所の比較を紹介します。 次に、2 つの主要なパフォーマンス指標とスナップショット ツールについて詳しく紹介します。
パフォーマンス指標は、主に CPU、メモリ、ヒープ領域、GC 指標に焦点を当てて紹介します。 v8 の GC 戦略と GC 最適化計画を理解します。- スナップショット ツールには、主にヒープ スナップショット、CPU スナップショット、クラッシュ時のコアダンプが含まれます
-
最後に、観察から簡単な問題を再現します。 、メモリ リークのケースの分析とトラブルシューティング、および一般的なメモリ リークの状況と解決策をまとめています。 この記事が、誰もが Node.js パフォーマンス監視システム全体を理解するのに役立つことを願っています。 ノード関連の知識の詳細については、nodejs チュートリアル を参照してください。
以上がなぜパフォーマンス監視が必要なのでしょうか? Node.js のパフォーマンス監視について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。