Nodejsメモリ治理について詳しく説明する.

3675 ワード

sの運行の宿主環境は違って、対応するメモリ管理に対する要求も違っています.宿主環境がブラウザである場合、ウェブページの運行時間が短いため、ユーザのマシン上だけに運行しています.メモリの使用量が多すぎたり、一定の内存漏えいがあったりしても、端末ユーザに大きな影響を与えることはありません.ホスト環境プログラミングサーバ(Node)の場合、そのコードは固定された何台かのマシン(中国式)で実行されています.また、実行時間は長いです.メモリの管理がうまくいかないと、メモリが膨張したり、メモリが漏れたりすることがあります.サーバー側の応答時間が長くなり、サービスcrashの場合もあります.
NodejsはV 8に基づいて構築されているので、Nodeで使用されるJavaScriptオブジェクトは基本的にV 8によって割り当てられ管理されています.V 8は占有メモリサイズに制約を与えた(64ビットのオペレーティングシステムでは、単一のNodeプロセスで使用できる最大ヒープメモリサイズは約1.5 GB).サーバのメモリが大きくても、V 8の制限により、Nodeはサーバのリソースを十分に利用できなくなります.それでも、なぜV 8はこのような制限をするのですか?このような制限をする原因は実はごみ回収メカニズムと関連しています.1.5 GBのゴミ回収ヒープのメモリヒープを例にとって、V 8は一回の小さなゴミ回収に50 ms以上必要です.一回のゴミ回収をすると1 s以上かかります.ゴミ回収過程でJavaScriptスレッドは実行停止状態にあることを知りたいです.長すぎる暫定時間はバックエンドサービスの性能に大きな影響を与えるので、V 8はこの点を考慮してメモリの積み込みを制限しました.それでもV 8は、メモリサイズをカスタマイズできるようにしてくれます.(--max-old-pace-size)、old-spaceは老生代、new-spaceは新生代を表します.

node --max-old-space-size=xxx index.js //   MB
//        -max-new-space-size         ,        
メモリの漏洩によってサーバーが頻繁に再起動されている場合、先にメモリの大きさを調整して位置決め問題のために時間を稼ぐことを提案します.結局、サービスの応答が遅いのは、直接エラーページに戻るよりもユーザーにとってもっと受け入れやすいです.
なぜ古い世代と新しい世代が必要ですか?
老生世代と新生代は実は分別式のゴミ回収メカニズムの中の異なっている世代です.ゴミ回収アルゴリズムがないので、すべてのシーンに適任できます.対象の生存周期は違っています.実は回収戦略が必要です.そして、それぞれの世代(新生代、老生代)のメモリにもっと適したアルゴリズムを施す.
新生代の対象は生存時間が短いが、老生時代の対象は長く、さらにメモリに常駐している.これに基づいて設計された新生代のメモリは一般的に旧世代のメモリより小さいです.V 8の中で新生代の最大メモリは32 M(64ビットシステムを例にして)、老生世代の最大メモリは140 MBです.V 8が実際に使用しているメモリのサイズは新生代+老生代に使用されたメモリの和(1432 M)であるが、V 8の最大値は実際に使用されているメモリの大きさより32 M(1464 MB)大きくなっている.
新生代はどうやってゴミを回収しますか?
新生代はScaavengeというゴミ回収アルゴリズムを採用しています.Sceavengeの具体的な実現において、主にCheneyアルゴリズムを採用しています.Cheneyアルゴリズムは新生代を二つに分けて、一つはFrom semispaceを使って、一つは暇です.を選択します.対象を作成する場合は、現在のFrom空間で配布しています.ゴミの回収が必要な場合は、From空間の中の生存対象を確認して、生存対象をTo空間にコピーし、From空間を空にして、FromとToを交換します.ゴミ回収の過程で、生存対象を二つのseispaceの間にコピーします.ライフサイクルの短いシーンで生存します.対象は全体の対象の中で比較的に小さいので、Scabiengeは複製生存の対象を採用していますが、Sceavengeはメモリの半分の空間しか利用できません.これは典型的な空間で時間を交換する表現です.
一つの対象が何度もゴミを回収して生きていると、ライフサイクルが長い対象とされています.一方、新生世代の方が小さいです.一方で、ライフサイクルの長いオブジェクトを複製するのも効率的ではないので、ライフサイクルの長いオブジェクトは老生世代に移されます.新生代の対象は老生世代に二つのオブジェクトがあります.1.対象は生ですか?ライフサイクルが長い対象(すでにゴミ回収を経験している)は2.Toのスペース使用比が25%を超えていますか?制限25%の原因はゴミ回収が完了したらToがFromになります.制限をしないとFromが早く光に使われることがあり、頻繁にゴミ回収ができ、効率にも影響します.
老生代はどうやってゴミを回収しますか?
旧世代は生存対象が大きな比重を占めているため、生存対象を操作するには適していません.Scabiengeアルゴリズムを使うのはあまり適していません.だから、高齢者世代はMark-SweepとMark-Comppactを結合する方式を採用しました.
マークはマークとクリアの2段階に分けられていますが、マーク段階ではすべてのオブジェクトを巡回し、生きているオブジェクトをマークし、クリア段階ではマークされていないオブジェクトは消去されます.マークはメモリリリースの問題を解決しましたが、Scaavengeのようにオブジェクトをコピーする操作がないためにメモリが断片化されてしまいます.Mark-comppactはメモリの断片を解決します.問題になります.Mark-Comppactは生きている対象を片側に移動し、移動が完了したら直接に境界外のメモリを消去します.これによって、大きな連続メモリがありますが、対象の移動に関連して、Mark-compectの速度はMark-Sweepより遅くなります.V 8は主にMark-Sweepを使用します.スペースが足りない場合は、新生代の中から来た対象を分けます.配合時にはMark-Comppactを使用します.
ゴミの回収過程では、アプリケーションの実行が一時停止されます.新生代自体はスペースが小さいため、複製の生存対象が少ないため、全量のゴミ回収を実行しても影響は大きくないです.しかし、老生世代の空間が大きく、生存対象も多く、一回の全量のゴミ回収を実行するのはアプリケーションの一時停止にとっては比較的長い時間です.マークは、マークが完了するまで、マークとアプリケーションが交互に実行されるように、インクリメンタルの更新方法に変更されました.ゴミは回収され、後の清掃作業が実行されます.整理作業は増分されていません.
開発者は強制的にゴミ回収を指定できますか?
答えは大丈夫です.nodeサービスを起動する時に使います.

$ node --expose-gc file.js
このように全体の対象にはゴミ回収を実行する機能があります.

global.gc();
より安全な書き方をおすすめします.

function forceGC()
 if (global.gc) {
  global.gc();
 } else {
  console.warn('No GC hook! Start your program as `node --expose-gc file.js`.');
 }
}
最後に参考資料を共有します.
https://speakerdeck.com/addyosmani/javascript-memory-management-masterclass
//www.jb 51.net/articale/140005.httm
https://www.xarg.org/2016/06/forcing-garbage-collection-in-node-js-and-javascript/