Heim  >  Artikel  >  Web-Frontend  >  Lassen Sie uns über den GC-Mechanismus (Garbage Collection) in Node.js sprechen

Lassen Sie uns über den GC-Mechanismus (Garbage Collection) in Node.js sprechen

青灯夜游
青灯夜游nach vorne
2022-11-29 20:44:082108Durchsuche

Wie führt

Node GC (Garbage Collection) durch? Der folgende Artikel führt Sie durch.

Lassen Sie uns über den GC-Mechanismus (Garbage Collection) in Node.js sprechen

GC, Garbage Collection, Garbage Collection. In der Programmierung bezieht sich der Begriff im Allgemeinen auf den automatischen Speicherrecyclingmechanismus, der regelmäßig nicht benötigte Daten löscht.

Node.js verwendet die darunter liegende V8-Engine. V8 ist eine leistungsstarke JavaScript-Engine, die von Google als Open-Source-Lösung bereitgestellt und in C++ geschrieben wurde. [Verwandte Tutorial-Empfehlungen: nodejs-Video-Tutorial]

Node.js-Speicher ist hauptsächlich in drei Teile unterteilt:

  • Coderaum: wo Codesegmente gespeichert werden;

  • Stack: temporäre Variablen, die durch den Funktionsaufruf generiert werden Stapel, Für einige Grundtypen wie Zahlen, Zeichenfolgen, boolesche Werte und Objektreferenzen (die Adresse wird gespeichert, nicht das Objekt selbst).

  • Heap: speichert Daten wie Objekte;

Heapspeicher

Node.js verwendet unten den Speicherrecyclingmechanismus von V8.

Zunächst werden alle Objekte in JS im Heap-Speicher gespeichert. Wenn der Prozess erstellt wird, wird eine anfängliche Größe des Heap-Speichers zugewiesen und dann werden unsere Objekte darin platziert.

Wenn immer mehr Objekte vorhanden sind, reicht der Heap-Speicher nicht aus und der Heap-Speicher wird dynamisch erweitert. Wenn eine maximale Grenze erreicht wird (heutzutage normalerweise 4 GB), tritt ein Heap-Überlauffehler auf und der Node.js-Prozess wird beendet.

Neue Generation und alte Generation

V8 unterteilt den Speicher zunächst in zwei Teile oder zwei Generationen:

  • Junge Generation: speichert einige Objekte mit kurzer Überlebenszeit;

  • Alte Generation: speichert Objekte, die dies getan haben eine lange Überlebenszeit oder sind dauerhaft.

Die neue Generation ist sehr klein und einige Objekte mit kurzer Überlebenszeit werden hier gespeichert. Normalerweise werden sie häufig recycelt (z. B. einige temporäre Objekte im Aufrufstapel von Funktionen).

Die Größe der neuen Generation kann über node --max-semi-space-size=SIZE index.js in MB geändert werden. node --max-semi-space-size=SIZE index.js 修改新生代的大小,单位为 MB。

另外,老生代则通过 --max-old-space-size=SIZE

Darüber hinaus verwendet die alte Generation --max-old-space-size=SIZE, um den Scavenge-Algorithmus der neuen Generation festzulegen

Die neue Generation verwendet den Scavenge-Algorithmus ein auf Kopie basierender Algorithmus.

Die neue Generation wird in zwei Räume aufgeteilt. Sie sind:

    Von Raum: Neu deklarierte Objekte werden hier platziert
  • Zum Raum: Raum, der für die Umsiedlung genutzt wird Das neu deklarierte Objekt wird im From-Bereich platziert. Durch den Zeiger befindet sich das vorherige Objekt in der Nähe des nächsten Objekts, sodass Sie sich keine Sorgen um den Speicher machen müssen Zersplitterung.
  • Die sogenannte Speicherfragmentierung bezieht sich auf eine ungleichmäßige Raumzuteilung, die zu einer großen Anzahl kleiner zusammenhängender Räume führt, die nicht in ein großes Objekt passen.

Wenn der From-Bereich fast voll ist, werden wir ihn durchqueren, um die aktiven Objekte zu finden und sie in den To-Bereich kopieren. Zu diesem Zeitpunkt ist der From-Bereich tatsächlich leer, und dann tauschen wir die Identitäten von From und To aus.

Wenn einige Objekte mehrmals kopiert werden, wird davon ausgegangen, dass sie eine längere Überlebenszeit haben, und sie werden in die alte Generation verschoben.

Der Vorteil dieses kopierbasierten Algorithmus besteht darin, dass er das Problem der Speicherfragmentierung sehr gut bewältigen kann. Der Nachteil besteht darin, dass er beim Verschieben von Speicherplatz etwas Platz verschwendet Nicht geeignet, um zu viel Speicherplatz zuzuweisen. Es handelt sich eher um einen Hilfs-GC.

Mark-Sweep und Mark-Compact

Der Raum der alten Generation ist viel größer als der der neuen Generation. Er enthält einige langlebige Objekte und verwendet den Mark-Sweep-Algorithmus (Mark Clearing).

Zunächst erfolgt die Markierungsphase. Suchen Sie alle zugänglichen Objekte aus dem Root-Set (Ausführungsstapel und globale Objekte) und markieren Sie sie als aktive Objekte. Nach dem Markieren ist es die Löschphase. Das Löschen der nicht markierten Objekte markiert tatsächlich die Speicheradresse als frei.

Dieser Ansatz führt zu einer Fragmentierung des freien Speicherplatzes. Wenn wir ein großes zusammenhängendes Objekt erstellen, können wir keinen Platz finden, an dem wir es ablegen können. Zu diesem Zeitpunkt muss Mark-Compact (Markierungskomprimierung) verwendet werden, um die fragmentierten aktiven Objekte zu integrieren.

Mark-Compact verschiebt alle aktiven Objektkopien an ein Ende, und auf der anderen Seite der Grenze befindet sich dann ein ganzer Block zusammenhängenden verfügbaren Speichers.

Angesichts der Tatsache, dass Mark-Sweep und Mark-Compact lange dauern und den JavaScript-Thread blockieren, führen wir normalerweise nicht alles auf einmal aus, sondern verwenden die inkrementelle Markierung (Inkrementelle Markierung). Das bedeutet, zeitweise zu markieren, kleine Schritte zu unternehmen und Garbage Collection und Anwendungslogik abzuwechseln.

Darüber hinaus führt V8 auch eine parallele Markierung und parallele Reinigung durch, um die Ausführungseffizienz zu verbessern.

Lassen Sie uns über den GC-Mechanismus (Garbage Collection) in Node.js sprechen

Speicherbezogene Informationen anzeigen

Über die Methode „process.memoryUsage“ können wir einige speicherbezogene Informationen abrufen.

process.memoryUsage();

Der Ausgabeinhalt ist:

{
  rss: 35454976,
  heapTotal: 7127040,
  heapUsed: 5287088,
  external: 958852,
  arrayBuffers: 11314
}

Erläuterung

  • rss: Größe des residenten Satzes (residente Satzgröße), einschließlich Codefragmenten, Heap-Speicher, Stapel usw.

  • heapTotal: die gesamte Heap-Speichergröße von V8;

  • heapUsed: der belegte Heap-Speicher;

  • external: die Speichergröße außer V8, die sich auf den von C++-Objekten belegten Speicher bezieht, z Pufferdaten.

  • arrayBuffers: Die Speichergröße im Zusammenhang mit ArrayBuffer und SharedArrayBuffer, die Teil von external ist. ArrayBufferSharedArrayBuffer 相关的内存大小,属于 external 的一部分。

以上数字的单位都是字节。

测试最大内存限制

写一个脚本,用一个定时器,让一个数组不停地变大,并打印堆内存使用情况,直到内存溢出。

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 中的实现的,是 Node.js 用 C++ 另外实现的,不通过 V8 分配内存,属于堆外内存。

我使用电脑是 macbook pro M1 Pro,Node.js 版本为 v16.17.0

Die Einheiten der oben genannten Zahlen sind Bytes.

Testen Sie das maximale Speicherlimit

Schreiben Sie ein Skript, verwenden Sie einen Timer, lassen Sie ein Array kontinuierlich wachsen und drucken Sie die Heap-Speichernutzung aus, bis der Speicher überläuft.

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
...

Es sollte besonders darauf geachtet werden, Buffer nicht zum Testen zu verwenden.

Da Buffer ein für Node.js einzigartiges Binärobjekt ist, wird es von Node.js separat mit C++ implementiert. Es reserviert keinen Speicher über V8 und gehört zum Off-Heap-Speicher. 🎜🎜Der Computer, den ich verwende, ist das MacBook Pro M1 Pro, die Node.js-Version ist v16.17.0 und die verwendete V8-Version ist 9.4.146.26-node.22 (über Prozess. versionen.v8 erhalten). 🎜🎜Das Ausgabeergebnis ist (einige redundante Informationen werden weggelassen): 🎜rrreee🎜Es ist ersichtlich, dass das Speicherlimit nach 4000 MB überschritten wird, ein Heap-Überlauf auftritt und der Prozess dann beendet wird. Beachten Sie, dass auf meinem Computer der standardmäßige maximale Speicher 4 GB beträgt. 🎜🎜Der tatsächliche maximale Speicher hängt von der Maschine ab, auf der er ausgeführt wird. Wenn die Speichergröße Ihrer Maschine 2 GB beträgt, wird der maximale Speicher auf 1,5 GB eingestellt. 🎜🎜Weitere Informationen zu Knoten finden Sie unter: 🎜nodejs-Tutorial🎜! 🎜

Das obige ist der detaillierte Inhalt vonLassen Sie uns über den GC-Mechanismus (Garbage Collection) in Node.js sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen