Node.js API に習熟していれば作業を早く進めることができますが、Node.js プログラムのメモリ フットプリントを深く理解することでさらに作業を進めることができます。
process.memoryUsage() を使用してメモリ使用量を確認し、毎秒更新することから始めましょう。
setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);
出力はバイト単位であるため、ユーザーフレンドリーではありません。メモリ使用量を MB:
にフォーマットして整理しましょう。
function formatMemoryUsageInMB(memUsage) { return { rss: convertToMB(memUsage.rss), heapTotal: convertToMB(memUsage.heapTotal), heapUsed: convertToMB(memUsage.heapUsed), external: convertToMB(memUsage.external) }; } const convertToMB = value => { return (value / 1024 / 1024).toFixed(2) + ' MB'; }; const logInterval = setInterval(() => { const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage()); console.log(`Memory Usage (MB):`, memoryUsageMB); }, 1000);
これで、次の出力を毎秒取得できるようになります:
Memory Usage (MB): { rss: '30.96 MB', // The actual OS memory used by the entire program, including code, data, shared libraries, etc. heapTotal: '6.13 MB', // The memory area occupied by JS objects, arrays, etc., dynamically allocated by Node.js // V8 divides the heap into young and old generations for different garbage collection strategies heapUsed: '5.17 MB', external: '0.39 MB' } Memory Usage (MB): { rss: '31.36 MB', heapTotal: '6.13 MB', heapUsed: '5.23 MB', external: '0.41 MB' }
V8 エンジンのメモリ使用量は、OS のメモリ管理とリソース割り当てポリシーだけでなく、独自の設定によっても制限されていることは誰もが知っています。
os.freemem() を使用すると、OS の空きメモリの量を確認できますが、それはすべて Node.js プログラムによって取得できるという意味ではありません。
console.log('Free memory:', os.freemem());
64 ビット システムの場合、Node.js V8 のデフォルトの最大古い領域サイズは約 1.4 GB です。これは、OS に使用可能なメモリがさらに多くても、V8 が自動的にこの制限を超えるメモリを使用することはないことを意味します。
ヒント: この制限は、環境変数を設定するか、Node.js の起動時にパラメーターを指定することで変更できます。たとえば、V8 でより大きなヒープを使用したい場合は、--max-old-space-size オプションを使用できます。
node --max-old-space-size=4096 your_script.js
この値は、実際の状況とシナリオに基づいて設定する必要があります。たとえば、大量のメモリを備えたマシンがスタンドアロンで展開されている場合と、多数のメモリの少ないマシンが分散方式で展開されている場合、この値の設定は明らかに異なります。
メモリがオーバーフローするまで配列に無限にデータを詰め込んでテストを実行し、いつそれが起こるかを見てみましょう。
const array = []; while (true) { for (let i = 0; i < 100000; i++) { array.push(i); } const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage()); console.log(`Memory Usage (MB):`, memoryUsageMB); }
プログラムを直接実行すると、次のようになります。データを少し追加すると、プログラムがクラッシュします。
Memory Usage (MB): { rss: '2283.64 MB', heapTotal: '2279.48 MB', heapUsed: '2248.73 MB', external: '0.40 MB' } Memory Usage (MB): { rss: '2283.64 MB', heapTotal: '2279.48 MB', heapUsed: '2248.74 MB', external: '0.40 MB' } # # Fatal error in , line 0 # Fatal JavaScript invalid size error 169220804 # # # #FailureMessage Object: 0x7ff7b0ef8070
混乱していますか? 1.4Gが限界じゃないですか?なぜ 2G を超えるのですか?実際、Node.js の 1.4GB 制限は V8 エンジンの歴史的な制限であり、初期の V8 バージョンと特定の構成に適用されます。最新の Node.js および V8 では、Node.js はシステム リソースに基づいてメモリ使用量を自動的に調整します。場合によっては、特に大規模なデータセットを処理する場合やメモリを大量に使用する操作を実行する場合には、1.4 GB をはるかに超える量が使用されることがあります。
メモリ制限を 512M に設定すると、RSS が 996 MB 付近に達するとオーバーフローします。
Memory Usage (MB): { rss: '996.22 MB', heapTotal: '993.22 MB', heapUsed: '962.08 MB', external: '0.40 MB' } Memory Usage (MB): { rss: '996.23 MB', heapTotal: '993.22 MB', heapUsed: '962.09 MB', external: '0.40 MB' } <--- Last few GCs ---> [22540:0x7fd27684d000] 1680 ms: Mark-sweep 643.0 (674.4) -> 386.8 (419.4) MB, 172.2 / 0.0 ms (average mu = 0.708, current mu = 0.668) allocation failure; scavenge might not succeed [22540:0x7fd27684d000] 2448 ms: Mark-sweep 962.1 (993.2) -> 578.1 (610.7) MB, 240.7 / 0.0 ms (average mu = 0.695, current mu = 0.687) allocation failure; scavenge might not succeed <--- JS stacktrace ---> FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
要約すると、より正確には、Node.js のメモリ制限はヒープ メモリ制限を指します。これは、V8 によって割り当てられた JS オブジェクト、配列などが占有することができる最大メモリです。
Node.js プロセスが占有できるメモリの量はヒープ メモリのサイズによって決まりますか?いいえ!読み続けてください。
テストでは、プログラムがクラッシュする前に配列が 2GB をわずかに超える量しか保持できないことがわかりました。では、3GB のファイルがある場合、それを一度に Node.js メモリに入れることはできないのでしょうか?
できます!
process.memoryUsage() を通じて外部メモリが確認されました。これは Node.js プロセスによって占有されていますが、V8 によって割り当てられていません。 3 GB のファイルをそこに置く限り、メモリ制限はありません。どうやって?バッファーを使用できます。 Buffer は、JS オブジェクトやデータではなく C を使用してメモリを割り当てる Node.js の C 拡張モジュールです。
これがデモです:
setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);
3 GB のメモリを割り当てた場合でも、プログラムは引き続きスムーズに実行されます。この外部メモリは Node.js によって制限されるのではなく、オペレーティング システムの割り当てメモリ制限によって制限されるため、Node.js プログラムは 5 GB 以上のメモリを占有します。 (したがって、バッファでもメモリ不足になる可能性があるため、ただ暴れることはできません。本質は、ストリームで大きなデータを処理することです)。
Node.js では、Buffer オブジェクトのライフサイクルは JavaScript オブジェクトに関連付けられています。 Buffer オブジェクトへの JavaScript 参照が削除されると、V8 ガベージ コレクターはそのオブジェクトをリサイクル可能としてマークしますが、Buffer オブジェクトの基礎となるメモリはすぐには解放されません。通常、C 拡張機能のデストラクターが呼び出されるとき (たとえば、Node.js のガベージ コレクション プロセス中)、メモリのこの部分が解放されます。ただし、このプロセスは V8 のガベージ コレクションと完全には同期していない可能性があります。
function formatMemoryUsageInMB(memUsage) { return { rss: convertToMB(memUsage.rss), heapTotal: convertToMB(memUsage.heapTotal), heapUsed: convertToMB(memUsage.heapUsed), external: convertToMB(memUsage.external) }; } const convertToMB = value => { return (value / 1024 / 1024).toFixed(2) + ' MB'; }; const logInterval = setInterval(() => { const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage()); console.log(`Memory Usage (MB):`, memoryUsageMB); }, 1000);
要約: Node.js のメモリ使用量は、JS ヒープ メモリ使用量 (V8 のガベージ コレクションによって決定)、C によるメモリ割り当てで構成されます
世代別ガベージ コレクション戦略は、最新のプログラミング言語の実装で広く普及しています。世代別ガベージ コレクションのような同様の戦略は、Ruby、.NET、Java にも見られます。ガベージ コレクションが発生すると、多くの場合「世界が停止する」状況が発生し、必然的にプログラムのパフォーマンスに影響を与えます。ただし、この設計はパフォーマンスの最適化を念頭に置いて考えられています。
メモリの割り当ては From 内で行われます。ガベージ コレクション中に、From のライブ オブジェクトが検査されて To にコピーされ、その後、非ライブ オブジェクトが解放されます。以降のコレクションでは、To のライブ オブジェクトが From にレプリケートされ、その時点で To が From にモーフィングされ、その逆も同様です。ガベージ コレクション サイクルごとに、From と To が交換されます。このアルゴリズムは、コピー プロセス中にライブ オブジェクトのみを複製するため、メモリの断片の生成を回避します。
では、変数の生存性はどのように判断されるのでしょうか?到達可能性分析が機能します。例として次のオブジェクトを考えてみましょう:
到達可能性分析のコンテキスト:
確かに、参照カウントは補助的な手段として機能します。それにもかかわらず、循環参照が存在する場合、オブジェクトの真の活性度を正確に確認できません。
旧世代のメモリでは、オブジェクトは一般的にあまりアクティブではありません。ただし、古い世代のメモリがいっぱいになると、マークスイープ アルゴリズムを通じて古い世代のメモリ (メジャー GC) のクリーンアップがトリガーされます。
マーク スイープ アルゴリズムは、マーキングとスイープの 2 つのフェーズで構成されます。マーキング フェーズでは、V8 エンジンはヒープ内のすべてのオブジェクトを走査し、ライブ オブジェクトにタグを付けます。スイープ フェーズでは、マークされていないオブジェクトのみがクリアされます。このアルゴリズムの利点は、古い世代の無効なオブジェクトの割合が比較的小さいため、スイープ フェーズにかかる時間が比較的短いことです。ただし、その欠点は、圧縮せずにクリアのみであるため、メモリ空間が不連続になる可能性があり、大きなオブジェクトにメモリを割り当てるのが不便になることです。
この欠点によりメモリの断片化が発生し、別のアルゴリズムである Mark-Compact の採用が必要になります。このアルゴリズムは、すべてのライブ オブジェクトを一方の端に移動し、境界の右側にある無効なメモリ領域を一気に消去することで、完全かつ継続的に利用可能なメモリ領域を取得します。これにより、多数のライブ オブジェクトの移動により多くの時間がかかるという犠牲は伴いますが、マークスイープ アルゴリズムによって引き起こされる可能性のあるメモリの断片化の問題が解決されます。
この投稿が役立つと思われた場合は、高評価をお願いします。 :D
以上がNode.js のメモリ制限とは正確には何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。