Heim > Artikel > Web-Frontend > Warum brauchen Sie eine Leistungsüberwachung? Lassen Sie uns über die Leistungsüberwachung von Node.j sprechen
Warum benötigen Sie eine Leistungsüberwachung? Dieser Artikel führt Sie durch die Leistungsüberwachung von Node.jsIch hoffe, er wird Ihnen hilfreich sein!
KnotenAls serverseitige Laufzeit für Javascript bereichert es die Anwendungsszenarien von Javascript erheblich.
Aber die Node.js-Laufzeit selbst ist eine Black Box. Wir können den Status der Laufzeit nicht erkennen und es ist auch schwierig, Online-Probleme zu reproduzieren. Daher ist die Leistungsüberwachung der Grundstein für den „normalen Betrieb“ von Node.js-Anwendungen. Es können nicht nur jederzeit verschiedene Laufzeitindikatoren überwacht werden, sondern es kann auch bei der Behebung ungewöhnlicher Szenarioprobleme hilfreich sein.
Komponenten
Erfassung und Anzeige von Leistungsindikatoren
Prometheus
Prom-Client
ist die NodeJS-Implementierung von Prometheus, die zum Sammeln von Leistungsindikatoren verwendet wird.alinode ist eine Erweiterung, die mit offiziellen Nodejs kompatibel ist. Zur Laufzeit werden einige zusätzliche Funktionen bereitgestellt:
Es integriert agentx
+process.cpuUsage()
abgerufen werden. Die Einheit des Rückgabewerts ist Mikrosekunden. Benutzer: Die vom Prozess selbst während der Ausführung verbrauchte CPU-Zeit. System: Die vom System verbrauchte CPU-Zeit wenn der Prozess ausgeführt wird .png" title="166021971753417Warum brauchen Sie eine Leistungsüberwachung? Lassen Sie uns über die Leistungsüberwachung von Node.j sprechen" alt="3.png"/>process.memoryUsage ()
Sie können die Speicherzuordnungsdaten des aktuellen Prozesses abrufen. Die Einheit der Rückgabewert ist Bytes. rss: residenter Speicher, die vom Knotenprozess zugewiesene Gesamtspeichergröße. heapTotal: die von v8 beantragte Heap-Speichergröße.
Wie Sie auf dem Bild oben sehen können, enthält rss
Codesegmente (Code Segment
), Stapelspeicher (Stack
) und Heapspeicher ( Heap
)rss
包含代码段(Code Segment
)、栈内存(Stack
)、堆内存(Heap
)
通过v8.getHeapStatistics()
和v8.getHeapSpaceStatistics()
可以获取v8堆内存和堆空间的分析数据,下图展示了v8的堆内存组成分布:
堆内存空间先划分为空间(space),空间又划分为页(page),内存按照1MB对齐进行分页。
New Space:新生代空间,用来存放一些生命周期比较短的对象数据,平分为两个空间(空间类型为semi space
):from space
,to space
Old Space:老生代空间,用来存放New Space
晋升的对象
Code Space:存放v8 JIT编译后的可执行代码
Map Space:存放Object指向的隐藏类的指针对象,隐藏类指针是v8根据运行时记录下的对象布局结构,用于快速访问对象成员
Large Object Space:用于存放大于1MB而无法分配到页的对象
v8的垃圾回收算法分为两类:
Mark-Sweep-Compact
算法,用于老生代的对象回收Scavenge
算法,用于新生代的对象回收前提:New space
分为from
和to
两个对象空间
触发时机:当New space
空间满了
步骤:
在from space
中,进行宽度优先遍历
发现存活(可达)对象
Old space
to space
中当复制结束时,to space
中只有存活的对象,from space
就被清空了
交换from space
和to space
,开始下一轮Scavenge
适用于回收频繁,内存不大的对象,典型的空间换时间的策略,缺点是浪费了多一倍的空间
三个步骤:标记、清除、整理
触发时机:当Old space
空间满了
步骤:
Marking(三色标记法)
marking queue
(显式栈)中,并将这些对象标记为灰色marking queue
pop
出来,并标记为黑色push
到marking queue
V8-Heap-Speicher und Heap-Speicherplatz können über v8.getHeapStatistics()
und v8.getHeapSpaceStatistics() Analysedaten, die folgende Abbildung zeigt die Heap-Speicherzusammensetzungsverteilung von Version 8:
semi space
): from space, <code>to space
New Space-Code verwendet wird >Heraufgestuftes Objekt🎜🎜🎜🎜Code Space: Speichert den von v8 JIT kompilierten ausführbaren Code🎜🎜🎜🎜Map Space: Speichert das Zeigerobjekt der versteckten Klasse, auf die Object zeigt. Der Zeiger der versteckten Klasse ist das von v8 aufgezeichnete Objekt die Laufzeit-Layoutstruktur für schnellen Zugriff auf Objektmitglieder🎜🎜🎜🎜Großer Objektraum: Wird zum Speichern von Objekten verwendet, die größer als 1 MB sind und nicht Seiten zugewiesen werden können🎜🎜🎜<h3 data-id="heading-8"><strong>GC strong></strong></h3>Die Garbage-Collection-Algorithmen von 🎜v8 sind in zwei Kategorien unterteilt: 🎜🎜🎜Major GC: Verwendet den <code>Mark-Sweep-Compact
-Algorithmus für das Recycling von Objekten der alten Generation 🎜🎜Minor GC: Verwendet den Scavenge
-Algorithmus für das Objektrecycling in der neuen Generation🎜🎜Neuer Bereich ist in zwei Objektbereiche unterteilt: <code>von
und bis
🎜🎜Auslösezeit: wenn der Neuer Bereich
-Bereich voll ist🎜🎜Schritte: 🎜🎜🎜🎜Führen Sie in aus dem Weltraum
eine Breitendurchquerung 🎜🎜🎜🎜 durch und stellen Sie fest, dass das überlebende (erreichbare) Objekt 🎜🎜🎜 einmal überlebt hat (einmal Scavange erlebt hat) und zu Altes Leerzeichen
🎜🎜Andere werden nach in Leerzeichen
kopiert 🎜🎜🎜🎜🎜Wenn der Kopiervorgang endet, gibt es nur noch überlebende Objekte in in Leerzeichen
, from space
wird gelöscht🎜🎜🎜🎜Tausch from space
und to space
und starte die nächste Runde von Scavenge
🎜🎜 🎜 🎜Geeignet für Objekte mit häufigem Recycling und kleinem Speicher. Es handelt sich um eine typische Raum-für-Zeit-Strategie. Der Nachteil besteht darin, dass doppelt so viel Platz verschwendet wird🎜Alter Speicherplatz
voll ist🎜🎜Schritte: 🎜🎜🎜🎜 Markierung (dreifarbige Markierungsmethode)🎜🎜🎜Weiß: Stellt recycelbare Objekte dar.🎜🎜Schwarz: Stellt nicht recycelbare Objekte dar, und die von ihnen generierten Referenzen wurden gescannt.🎜🎜Grau: Stellt nicht recycelbare Objekte und die von generierten Referenzen dar Sie wurden noch nicht gescannt. Nach dem Scannen legen Sie die Objekte, auf die direkt vom V8-Stammobjekt verwiesen wird, in eine Markierungswarteschlange
(expliziten Stapel) und markieren Sie diese Objekte als grau. Beginnen Sie mit der Tiefendurchquerung Von diesen Objekten aus jedes Mal, wenn Sie auf ein Objekt zugreifen, das Objekt aus der Markierungswarteschlange
pop
entfernen und es als schwarz markieren 🎜🎜 Markieren Sie dann alle weißen Objekte unter der Objektreferenz als grau, Push
in die Markierungswarteschlange
usw., bis alle Objekte auf dem Stapel entfernt sind. In der alten Generation sind nur noch zwei Objekte übrig: schwarz (nicht-). recycelbar) und weiß (recycelbar). PS: Wenn ein Objekt zu groß ist, um auf den Stapel geschoben zu werden, behält v8 das Objekt in Grau und überspringt es, wodurch der gesamte Stapel als überfüllt markiert wird Wenn es gelöscht wird, wird es erneut überquert, was einen zusätzlichen Scan des Heaps erfordert. Das Löschen weißer Objekte führt dazu, dass der Speicherplatz diskontinuierlich wirdOld space
的一端,这样清除出来的空间就是连续完整的在最开始v8进行垃圾回收时,需要停止程序的运行,扫描完整个堆,回收完内存,才会重新运行程序。这种行为就叫全停顿(Stop-The-World
)
虽然新生代活动对象较小,回收频繁,全停顿,影响不大,但是老生代存活对象多且大,标记、清理、整理等造成的停顿就会比较严重。
这个理念其实有点像React框架中的Fiber架构,只有在浏览器的空闲时间才会去遍历Fiber Tree执行对应的任务,否则延迟执行,尽可能少地影响主线程的任务,避免应用卡顿,提升应用性能。
由于v8对于新老生代的空间默认限制了大小
New space
默认限制:64位系统为32M,32位系统为16MOld space
默认限制:64位系统为1400M,32位系统为700M因此node
提供了两个参数用于调整新老生代的空间上限
--max-semi-space-size
:设置New Space
空间的最大值--max-old-space-size
:设置Old Space
空间的最大值node
也提供了三种查看GC日志的方式:
--trace_gc
:一行日志简要描述每次GC时的时间、类型、堆大小变化和产生原因--trace_gc_verbose
:展示每次GC后每个V8堆空间的详细状况--trace_gc_nvp
:每次GC的详细键值对信息,包含GC类型,暂停时间,内存变化等由于GC日志比较原始,还需要二次处理,可以使用AliNode团队开发的v8-gc-log-parser
对于运行程序的堆内存进行快照采样,可以用来分析内存的消耗以及变化
生成.heapsnapshot
文件有以下几种方式:
使用heapdump
使用v8的heap-profile
使用nodejs内置的v8模块提供的api
v8.getHeapSnapshot()
v8.writeHeapSnapshot(fileName)
生成的.heapsnapshot
Stop-The-World
) bezeichnet. 🎜🎜Obwohl die aktiven Objekte in der neuen Generation klein sind und häufig recycelt werden, hat der Punkt nur geringe Auswirkungen, jedoch auf die überlebenden Objekte in der alten Generation Es gibt viele und große Pausen, die durch Markieren, Reinigen, Ordnen usw. verursacht werden. 🎜Neuer Speicherplatz
Standardlimit: 32 MB für 64-Bit-System, 16 MB für 32-Bit-System 🎜🎜Alter Speicherplatz
Standardlimit: 1400 MB für 64-Bit-System, 1400 MB für 32 -Bit-System 700M🎜🎜Daher stellt node
zwei Parameter zum Anpassen der oberen Speicherplatzgrenze der neuen und alten Generation bereit🎜🎜🎜--max-semi-space-size: Legen Sie den Maximalwert von <code>New Space
fest. space🎜🎜--max-old-space-size
: Legen Sie den Maximalwert von Old Space fest. code> space🎜<h4 data-id="heading-14"><strong>GC-Protokolle anzeigen</strong></h4>🎜<code>node
bietet außerdem drei Möglichkeiten, GC anzuzeigen Protokolle: 🎜 🎜🎜--trace_gc
: Eine Protokollzeile beschreibt kurz die Zeit, den Typ, die Heap-Größenänderungen und die Ursachen jedes GC. 🎜🎜--trace_gc_verbose
: Zeigt die Ergebnisse jedes GC Detaillierter Status jedes V8-Heap-Speicherplatzes 🎜🎜--trace_gc_nvp
: Detaillierte Informationen zu Schlüssel-Wert-Paaren jedes GC, einschließlich GC-Typ, Pausenzeit, Speicheränderungen usw. 🎜🎜Aufgrund des GC-Protokolls ist es relativ primitiv und erfordert eine sekundäre Verarbeitung. Sie können v8-gc-log-parser🎜.heapsnapshot
-Dateien zu generieren: 🎜🎜 🎜🎜Verwenden Sie heapdump🎜🎜🎜v8.getHeapSnapshot()
🎜🎜🎜v8.writeHeapSnapshot(fileName)
🎜🎜🎜.heapsnapshot
-Datei kann in den Speicher der Chrome Devtools-Symbolleiste hochgeladen werden. Nach Auswahl wird das Ergebnis angezeigt wie unten gezeigt angezeigt: 🎜🎜🎜🎜Die Standardansicht ist die Ansicht Zusammenfassung
. Hier müssen wir auf die beiden Spalten ganz rechts achten: Flache Größe
und Beibehaltene Größe
Summary
视图,在这里我们要关注最右边两栏:Shallow Size
和 Retained Size
Shallow Size
:表示该对象本身在v8堆内存分配的大小Retained Size
:表示该对象所有引用对象的Shallow Size
之和当发现Retained Size
特别大时,该对象内部可能存在内存泄漏,可以进一步展开去定位问题
还有Comparison
视图是用于比较分析两个不同时段的堆快照,通过Delta
列可以筛选出内存变化最大的对象
对于运行程序的CPU进行快照采样,可以用来分析CPU的耗时及占比
生成.cpuprofile
文件有以下几种方式:
这是采集5分钟的CPU Profile样例
生成的.cpuprofile
文件,可以在Chrome devtools工具栏的Javascript Profiler
(不在默认tab,需要在工具栏右侧的更多中打开显示),选择上传文件后,展示结果如下图:
默认的视图是Heavy
视图,在这里我们看到有两栏:Self Time
和Total Time
Self Time
:代表此函数本身(不包含其他调用)的执行耗时Total Time
:代表此函数(包含其他调用函数)的总执行耗时当发现Total Time
和Self Time
偏差较大时,该函数可能存在耗时比较多的CPU密集型计算,也可以展开进一步定位排查
当应用意外崩溃终止时,系统会自动记录下进程crash掉那一刻的内存分配信息,Program Counter以及堆栈指针等关键信息来生成core文件
生成.core
文件的三种方法:
ulimit -c unlimited
打开内核限制node --abort-on-uncaught-exception
node启动添加此参数,可以在应用出现未捕获的异常时也能生成一份core文件gcore <pid></pid>
手动生成core文件获取.core
文件后,可以通过mdb、gdb、lldb等工具实现解析诊断实际进程crash的原因
llnode `which node` -c /path/to/core/dump
从监控可以观察到堆内存在持续上升,因此需要堆快照进行排查
根据heapsnapshot
可以分析排查到有一个newThing
的对象一直保持着比较大的内存
从代码中可以看到虽然unused
方法没有调用,但是newThing
对象是引用自theThing
,导致其一直存在于replaceThing
Shallow Size
: Gibt die Größe des Objekts selbst an, das im v8-Heap-Speicher zugewiesen istRetained Size
: Gibt den Shallow an aller referenzierten Objekte des Objekts Size
sumRetained Size
besonders groß ist, liegt möglicherweise ein Speicherverlust im Objekt vor. und Sie können weiter expandieren, um das Problem zu lokalisierenDelta
kann zum Filtern verwendet werden Herausfinden der Objekte mit den größten Speicheränderungen.cpuprofile
Datei, Sie können zu Javascript Profiler
in der Chrome-Devtools-Symbolleiste gehen (nicht auf der Standardregisterkarte, Sie benötigen). , um die Anzeige unter „Mehr“ auf der rechten Seite der Symbolleiste zu öffnen. Nach der Auswahl zum Hochladen der Datei sieht das Anzeigeergebnis wie folgt aus: 🎜🎜 Heavy
-Ansicht. Hier sehen wir, dass es zwei gibt Spalten: Selbstzeit
und Gesamtzeit
🎜Selbstzeit
: stellt die Ausführungszeit dieser Funktion selbst dar (mit Ausnahme anderer Aufrufe). )Gesamtzeit
: stellt die Gesamtausführungszeit dieser Funktion dar (einschließlich anderer aufrufender Funktionen) Selbstzeit
sind groß, die Funktion kann CPU-intensive Berechnungen haben, die viel Zeit in Anspruch nehmen. Sie können auch weitere Fehlerbehebungen durchführen🎜.core
-Dateien zu generieren:🎜ulimit -c unlimitedKernel-Grenzwerte aktivieren
node --abort-on-uncaught-Exception
Fügen Sie diesen Parameter beim Starten des Knotens hinzu, der in der Anwendung verwendet werden kann Eine Kerndatei kann wird auch generiert, wenn eine nicht abgefangene Ausnahme auftrittgcore <pid></pid>
Generieren Sie die Kerndatei manuell.core
-Datei können Sie mdb, gdb, lldb und andere Tools verwenden, um die Ursache des tatsächlichen Prozessabsturzes zu analysieren und zu diagnostizieren🎜llnode `which node` -c /path/to/core/dump
newThing
immer einen relativ großen Speicher beibehalten hat🎜unused
nicht aufgerufen wird, wird von theThing
auf das Objekt newThing
verwiesen, was dazu führt Es muss immer in replaceThing
vorhanden sein. Im Ausführungskontext der Funktion wird es nicht freigegeben. Dies ist ein typischer Speicherverlustfall, der durch Schließungen verursacht wird. Zusammenfassung: Zu den häufigen Speicherverlusten gehören die folgenden Situationen :🎜In den oben genannten Situationen müssen Sie daher sorgfältig abwägen, ob das Objekt im Speicher automatisch recycelt wird oder nicht. Wenn ja, ist eine manuelle Recycling erforderlich B. das manuelle Festlegen des Objekts auf null
, das Entfernen von Timern, das Aufheben der Bindung von Ereignis-Listenern usw.
In diesem Artikel wurde bisher eine detaillierte Einführung in das gesamte Leistungsüberwachungssystem von Node.js gegeben.
Zunächst werden die durch Leistungsüberwachung gelösten Probleme, ihre Komponenten und ein Vergleich der Vor- und Nachteile gängiger Lösungen vorgestellt.
Dann werden die beiden wichtigsten Leistungsindikatoren und Snapshot-Tools im Detail vorgestellt.
Abschließend wird ein einfacher Speicherverlustfall aus Beobachtung, Analyse und Fehlerbehebung reproduziert und häufige Speicherverlustsituationen und -lösungen zusammengefasst.
Ich hoffe, dieser Artikel kann jedem helfen, das gesamte Leistungsüberwachungssystem von Node.js zu verstehen.
Weitere Informationen zu Knoten finden Sie unter: nodejs-Tutorial!
Das obige ist der detaillierte Inhalt vonWarum brauchen Sie eine Leistungsüberwachung? Lassen Sie uns über die Leistungsüberwachung von Node.j sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!