ノード GC (ガベージ コレクション) はどのように機能しますか?次の記事で詳しく説明します。
GC、ガベージ コレクション、ガベージ コレクション。プログラミングでは、これは一般に、不要なデータを定期的に消去する自動メモリ リサイクル メカニズムを指します。
Node.js はその下で V8 エンジンを使用します。 V8 は、Google がオープンソース化した C で書かれた高性能 JavaScript エンジンです。 [関連チュートリアルの推奨事項: nodejs ビデオ チュートリアル ]
Node.js のメモリは主に 3 つの部分に分かれています:
コード スペース: ここでコード セグメントは格納されます 場所;
スタック: 関数呼び出しスタックによって生成される一時変数で、数値、文字列、ブール値、オブジェクト参照 (アドレスなど) などの基本的な型です。は保存されますが、オブジェクト自体は保存されません)。
ヒープ: オブジェクトとその他のデータを保存します。
基盤となる Node.js使用方法 それは V8 です V8 のメモリ再利用の仕組みを説明しましょう。
まず、JS 内のすべてのオブジェクトはヒープ メモリに保存されます。プロセスが作成されると、初期サイズのヒープ メモリが割り当てられ、その中にオブジェクトが配置されます。
オブジェクトが増えると、ヒープ メモリが足りなくなり、ヒープ メモリが動的に拡張されます。最大制限 (現在では通常 4 GB) に達すると、ヒープ オーバーフロー エラーが発生し、Node.js プロセスが終了します。
V8 はまずメモリを 2 つの部分、つまり 2 つの世代に分割します。
若い世代: 短い生存時間で一部のオブジェクトを保存します;
旧世代 (旧世代): 長い生存時間または長期永続性でオブジェクトを保存します。
新しい世代は非常に小さく、生存時間の短いいくつかのオブジェクトがここに保存されます。これらは通常、頻繁にリサイクルされます (関数の呼び出しスタック内の一部の一時オブジェクトなど)。
新しい世代のサイズは、
node --max-semi-space-size=SIZEindex.js
を通じて変更できます。単位は MB です。さらに、古い世代は
--max-old-space-size=SIZE
を使用して、新しい世代の
新しい世代では、コピーに基づくアルゴリズムであるスカベンジ アルゴリズムが使用されます。
新しい世代は 2 つのスペースに分割されます。このスペースはセミスペースと呼ばれます。それらは次のとおりです:
スペースから: 新しく宣言されたオブジェクトはここに配置されます
To スペース: 再配置に使用されるスペース
新しく宣言されたオブジェクトは From スペースに配置され、From スペース内のオブジェクトは密に配置されます。ポインタでは、前のオブジェクトは次のオブジェクトに近く、メモリは連続しているため、メモリの断片化を心配する必要はありません。
いわゆるメモリの断片化とは、不均一なスペース割り当てを指し、その結果、大きなオブジェクトに収まらない小さな連続スペースが多数発生します。
From スペースがほぼいっぱいになったら、トラバースしてアクティブなオブジェクトを見つけ、それらを To スペースにコピーします。この時点では、From スペースは実際には空であるため、From と To の ID を交換します。
一部のオブジェクトが複数回コピーされた場合、それらのオブジェクトは存続期間が長いとみなされ、古い世代に移動されます。
このコピーベースのアルゴリズムの利点は、メモリの断片化の問題をうまく処理できることです。欠点は、移動空間の場所としてスペースを無駄にすることです。また、コピーには時間がかかるため、メモリ空間は補助的な GC のようなものであり、大きすぎる割り当てには適していません。
旧世代のスペースは新世代のスペースよりもはるかに大きく、長期にわたって存続するオブジェクトがいくつか含まれています。マークスイープ (マーク除去) アルゴリズムが使用されます。
最初はマーキング段階です。ルート セットからアクセス可能なすべてのオブジェクト (実行スタックおよびグローバル オブジェクト) を検索し、アクティブ オブジェクトとしてマークします。
マークを付けた後、クリア フェーズになります。マークされていないオブジェクトをクリアすると、実際にはメモリ アドレスが空きとしてマークされます。
このアプローチは、空きメモリ空間の断片化につながります. 大きな連続オブジェクトを作成すると、それを置く場所が見つからなくなります。このとき、断片化したアクティブオブジェクトを統合するには、Mark-Compact (マークコンパクション) を使用する必要があります。
Mark-Compact は、すべてのアクティブなオブジェクトのコピーを一方の端に移動し、境界の反対側は連続した使用可能なメモリのブロック全体になります。
Mark-スイープとマーク-コンパクトは時間がかかり、JavaScript スレッドをブロックすることを考慮して、通常は一度にすべてを実行せず、増分マーキング (増分マーキング) を使用します。つまり、断続的にマークを付け、小さなステップを踏み、ガベージ コレクションとアプリケーション ロジックを交互に実行する必要があります。
さらに、V8 では、実行効率を向上させるために、並行マーキングと並行クリーニングも実行されます。
process.memoryUsage();
{ rss: 35454976, heapTotal: 7127040, heapUsed: 5287088, external: 958852, arrayBuffers: 11314 }
ArrayBuffer および
SharedArrayBuffer に関連するメモリ サイズ。
const format = function (bytes) { return (bytes / 1024 / 1024).toFixed(2) + " MB"; }; const printMemoryUsage = function () { const memoryUsage = process.memoryUsage(); console.log( `heapTotal: ${format(memoryUsage.heapTotal)}, heapUsed: ${format( memoryUsage.heapUsed )}` ); }; const bigArray = []; setInterval(function () { bigArray.push(new Array(20 * 1024 * 1024)); printMemoryUsage(); }, 500);
テストに Buffer を使用しないように特別な注意を払う必要があります。 Buffer は Node.js に固有のバイナリ オブジェクトであるため、V8 には実装されていません。C の Node.js によって別途実装されます。V8 を通じてメモリを割り当てず、オフヒープ メモリに属します。 。私が使用しているコンピューターは macbook pro M1 Pro、Node.js バージョンは
v16.17.0、使用されている V8 バージョンは 9.4.146.26-node.22 (経由) process.versions .v8 が取得します)。
heapTotal: 164.81 MB, heapUsed: 163.93 MB heapTotal: 325.83 MB, heapUsed: 323.79 MB heapTotal: 488.59 MB, heapUsed: 483.84 MB ... heapTotal: 4036.44 MB, heapUsed: 4003.37 MB heapTotal: 4196.45 MB, heapUsed: 4163.29 MB <--- Last few GCs ---> [28033:0x140008000] 17968 ms: Mark-sweep 4003.2 (4036.4) -> 4003.1 (4036.4) MB, 2233.8 / 0.0 ms (average mu = 0.565, current mu = 0.310) allocation failure scavenge might not succeed [28033:0x140008000] 19815 ms: Mark-sweep 4163.3 (4196.5) -> 4163.1 (4196.5) MB, 1780.3 / 0.0 ms (average mu = 0.413, current mu = 0.036) allocation failure scavenge might not succeed <--- JS stacktrace ---> FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory ...
nodejs チュートリアル を参照してください。
以上がNode.js の GC (ガベージ コレクション) メカニズムについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。