[LINUX] Wenn der verfügbare Speicher des Knotens erhöht wird, kann er durch vm.max_map_count begrenzt werden

Umgebung

Zusammenfassung

Unter Ubuntu mit 64 GB Speicher wurden 48 GB je nach Programminhalt erhöht, als ich den Knoten mit dem maximalen Speicherverbrauch auf 48 GB mit der Option "--max-old-space-size" verschoben habe. Es gab zwei Verhaltensweisen, eines war, als es aufgebraucht war, und das andere war, als ein Speicherfehler auftrat, als ungefähr 20 GB verbraucht wurden.

Die Ursache war der Unterschied in der Speicherzuweisungsmethode aufgrund des Unterschieds in der Funktionsweise des Programms, und in dem Programm, das den Speicher nicht belegen konnte, erzeugte der mMap-Systemaufruf ENOMEM, da die Anzahl der Zuordnungen überschritten wurde.

Durch Erweitern der maximalen Anzahl von Zuordnungen des Systems mit dem Befehl sudo sysctl -w vm.max_map_count = 655300 kann das Programm den angegebenen Speicher belegen.

Einführung

Anzeige des Speicherverbrauchs

Der folgende Blockcode zeigt den Einzeiler, der den Speicherverbrauch des auf bash ausgeführten Knotens anzeigt.

python


$ node -e 'console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
0.02828216552734375

Das Folgende ist ein Blick auf den Speicherverbrauch nach dem Erstellen eines Arrays, das aus 10.000.000 Nullen besteht.

python


$ node -e 'var a = Array.from({length: 10000000}, v => 0); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
0.10385513305664062

Der Speicherverbrauch ist leicht von 0,028 GB auf 0,103 GB gestiegen. Eine 10-Mega-Ganzzahl erzeugte einen Verbrauch von etwa 70 MB.

Wenn Sie nicht "--max-old-space-size" angeben, wird das Speicherlimit sofort erreicht.

Was ist, wenn Sie ein Array von 100 0s anstelle von 0s generieren?

python


$ node -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 100}, v => 0)); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

Diese Umgebung hat ungefähr 64 GB Speicher, daher halte ich es für physisch ausreichend. Wenn der Knoten jedoch ungefähr 5 GB verbraucht, wird der folgende Fehler angezeigt.

python


$ node -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 100}, v => 0)); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

<--- Last few GCs --->

[27037:0x648f650]    45191 ms: Scavenge 4093.1 (4100.7) -> 4093.0 (4101.5) MB, 5.7 / 0.0 ms  (average mu = 0.125, current mu = 0.081) allocation failure
[27037:0x648f650]    45204 ms: Scavenge (reduce) 4094.0 (4105.5) -> 4094.0 (4106.2) MB, 5.6 / 0.0 ms  (average mu = 0.125, current mu = 0.081) allocation failure
[27037:0x648f650]    45218 ms: Scavenge (reduce) 4095.1 (4100.5) -> 4095.0 (4103.7) MB, 6.2 / 0.0 ms  (average mu = 0.125, current mu = 0.081) allocation failure


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x9fd5f0 node::Abort() [node]
 2: 0x94a45d node::FatalError(char const*, char const*) [node]
 3: 0xb7099e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
 4: 0xb70d17 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
 5: 0xd1a905  [node]
 6: 0xd1b48f  [node]
 7: 0xd294fb v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 8: 0xd2d0bc v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
 9: 0xcfb7bb v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]
10: 0x1040c4f v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
11: 0x13cc8f9  [node]
Aborted (core dumped)

Wenn Sie Google mit "Ineffektive Mark-Compacts in der Nähe des Heap-Limits" durchsuchen. Zuordnung fehlgeschlagen - JavaScript-Heap nicht genügend Speicher ", können Sie die Obergrenze des Speichers, den der Knoten mit" --max-old-space-size "belegen kann, wie folgt erweitern. Ich habe viele Artikel bekommen.

Diese Umgebung verfügt über 64 GB Speicher. Versuchen wir also, bis zu 48 GB zu verbrauchen.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 100}, v => 0)); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
8.26059341430664

Nach dem Verbrauch von 8,2 GB Speicher endete es normal.

Der angegebene Speicher kann nicht belegt werden

Was ist, wenn Sie ein "Array bestehend aus 1000 leeren Arrays" anstelle eines "Arrays bestehend aus 100 Nullen" generieren? Erstens sollte der Speicherverbrauch das 10-fache betragen, sodass der Speicher nicht ausreichen sollte. Versuchen Sie außerdem, anstelle von 0 hier ein leeres Array zu generieren.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 1000}, v => [])); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

Es wird erwartet, dass 48 GB verbraucht sind und aufgrund von Speichermangel ein Fehler auftritt. Bevor die 48 GB aufgebraucht sind, kann es sein, dass die Betriebsgeschwindigkeit aufgrund der Speicherbereinigung extrem langsam wird.


Das Ergebnis endete mit dem folgenden Fehler, wenn nur 16,8 GB verbraucht wurden: Aus den Ergebnissen im vorherigen Abschnitt geht hervor, dass der Effekt von "--max-old-space-size" auftritt.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 1000}, v => [])); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

<--- Last few GCs --->

[10261:0x648f730]    77051 ms: Scavenge 16379.4 (16414.4) -> 16377.4 (16428.1) MB, 32.2 / 0.0 ms  (average mu = 0.794, current mu = 0.795) allocation failure
[10261:0x648f730]    77103 ms: Scavenge 16393.4 (16428.1) -> 16395.3 (16430.4) MB, 27.8 / 0.0 ms  (average mu = 0.794, current mu = 0.795) allocation failure
[10261:0x648f730]    77189 ms: Scavenge 16395.3 (16430.4) -> 16393.6 (16441.6) MB, 86.3 / 0.0 ms  (average mu = 0.794, current mu = 0.795) allocation failure


<--- JS stacktrace --->

FATAL ERROR: Scavenger: semi-space copy Allocation failed - JavaScript heap out of memory
Segmentation fault (core dumped)

Nach mehrmaliger Ausführung kann die Fehleranweisung auch zufällig und wie folgt sein.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 1000}, v => [])); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

Warum werden nur 16,8 GB verwendet, selbst wenn 48 GB mit "--max-old-space-size" angegeben sind? Wie kann ich 48 GB verbrauchen?

Umfrage

Code, der 48 GB verbrauchen kann, und Code, der nicht verbraucht werden kann

Der folgende Code erhöht die Anzahl der Impressionen des Speicherverbrauchs.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => []); });'

Als ich es ausführte, wurde der Speicherverbrauch angezeigt und es endete abnormal bei etwa 20 GB.

python


<Ausgelassen>

20.046581268310547
20.084598541259766
20.122615814208984
20.160381317138672
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

Mit dem folgenden Code wird "ein Array von 1s" anstelle von "einem Array von leeren Arrays" generiert.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => 1); });'

Dieser Code hört nicht bei 20 GB auf, er hat nicht mehr die angegebenen 48 GB Speicher und endet! Da der Speicherverbrauch 46 GB überschritt, wurde die Speicherbereinigung häufig gestottert, und der Speicherverbrauch stieg nicht leicht an ~~ Achilles und Kame ~~.

python


<Ausgelassen>

48.053977966308594
48.06153106689453
48.06904602050781
48.076324462890625
48.08384323120117

<--- Last few GCs --->

<Ausgelassen>

<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x9fd5f0 node::Abort() [node]

<Ausgelassen>

17: 0x1018255 v8::internal::Runtime_NewArray(int, unsigned long*, v8::internal::Isolate*) [node]
18: 0x13cc8f9  [node]
Aborted (core dumped)

Das einzige, was sich geändert hat, ist, ob der tiefste Teil der Datenstruktur ein leeres Array oder eine der Zahlen ist. In beiden Fällen ist überhaupt nicht genügend physischer Speicher vorhanden, sodass sich die endgültige abnormale Beendigung nicht ändert.

std :: bad_alloc scheint ein Fehler zu sein, wenn neu in C ++ fehlschlägt

Eine Google-Suche nach "std :: bad_alloc" ergab, dass es von neu in C ++ stammt.

Der Knoten ist in C ++ geschrieben, was bedeutet, dass es einen Ort gibt, an dem Neues gemacht wird. std :: bad_alloc kann von einer C ++ - try-Anweisung erfasst werden, kann jedoch sowohl an den erfassten als auch an den nicht erfassten Speicherorten auftreten, da der allgemeine Speicherort neu ist. Ich gehe davon aus, dass dies die Ursache dafür ist, dass sich die Fehleranweisung zufällig ändert.

Als ich strace betrachtete, stellte ich fest, dass mMap den Fehler ENOMEM gab

strace ist ein Befehl, der einen Systemaufruf eines Prozesses ausgeben kann.

Vorerst habe ich es vor den Knoten gestellt und wie folgt ausgeführt.

python


$ strace node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => 1); });'

Das verärgerte Protokoll wird ausgegeben, aber das Ende ist wie folgt.

python


<Ausgelassen>

mprotect(0x29960bbc0000, 262144, PROT_READ|PROT_WRITE) = 0
brk(0x1bf9a000)                         = 0x1bf9a000
mmap(0x2ebbbafc0000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x2ebbbafc0000
munmap(0x2ebbbb000000, 258048)          = 0
mprotect(0x2ebbbafc0000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x3ba59c200000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x3ba59c200000
munmap(0x3ba59c240000, 258048)          = 0
mprotect(0x3ba59c200000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x3f3009d40000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x3f3009d40000
munmap(0x3f3009d80000, 258048)          = 0
mprotect(0x3f3009d40000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x330b57380000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x330b57380000
munmap(0x330b573c0000, 258048)          = 0
mprotect(0x330b57380000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x207b9d440000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x207b9d440000
munmap(0x207b9d480000, 258048)          = 0
mprotect(0x207b9d440000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x300db2380000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x300db2380000
munmap(0x300db23c0000, 258048)          = 0
mprotect(0x300db2380000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x8e44e340000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x8e44e340000
munmap(0x8e44e380000, 258048)           = 0
mprotect(0x8e44e340000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x1a79a5c00000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x1a79a5c00000
munmap(0x1a79a5c40000, 258048)          = 0
mprotect(0x1a79a5c00000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x9abb4d00000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(0x9abb4d00000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mprotect(0xafbb8382000, 86016, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb83c2000, 249856, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb8402000, 4096, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb8442000, 4096, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb8482000, 4096, PROT_READ|PROT_WRITE) = 0
mmap(NULL, 1040384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
brk(0x1c0a2000)                         = 0x1bf9a000
mmap(NULL, 1175552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(0x7efbe4000000, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 1040384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
futex(0x7efbfebab1a0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
write(2, "terminate called after throwing "..., 48terminate called after throwing an instance of ') = 48
write(2, "std::bad_alloc", 14std::bad_alloc)          = 14
write(2, "'\n", 2'
)                      = 2
write(2, "  what():  ", 11  what():  )             = 11
write(2, "std::bad_alloc", 14std::bad_alloc)          = 14
write(2, "\n", 1
)                       = 1
rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [], 8) = 0
getpid()                                = 15400
gettid()                                = 15400
tgkill(15400, 15400, SIGABRT)           = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGABRT {si_signo=SIGABRT, si_code=SI_TKILL, si_pid=15400, si_uid=1003} ---
+++ killed by SIGABRT (core dumped) +++
Aborted (core dumped)

Hier können Sie aus der folgenden Zeile sehen, dass der Fehler "ENOMEM" in dem Teil namens mMap erscheint. Warum ist es nicht möglich, Speicher zuzuweisen, wenn überschüssiger Speicher vorhanden ist?

python


mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)

Die Bedingungen für ENOMEM sind unzureichender Speicher und überschüssige Zuordnungsnummer.

Wichtige Informationen wurden in den Mann von mmap geschrieben.

ENOMEM Es ist kein freier Speicher vorhanden, oder die Anzahl der Zuordnungen für den zu verarbeitenden Prozess hat das Maximum überschritten.

Wenn "die Anzahl der Zuordnungen die maximale Anzahl überschreitet", wird ENOMEM ausgegeben, auch wenn zu viel Speicher vorhanden ist. Der Fehler selbst ist ein häufiges ENOMEM, daher ist er aus Sicht eines Programms wahrscheinlich nicht zu unterscheiden. Es ist verständlich, dass die Fehlermeldung "JavaScript-Heap nicht genügend Speicher" lautet.

Sie können die maximale Anzahl von Zuordnungen mit sudo sysctl -w vm.max_map_count = erhöhen

Ein Weg, um die maximale Anzahl von Zuordnungen zu erhöhen, wurde von der folgenden Stelle erhalten.

Den aktuellen Wert finden Sie mit sysctl vm.max_map_count. Sie können den Wert mit sudo sysctl -w vm.max_map_count = 65536 einstellen.

Die Änderungen wirken sich auf das gesamte System aus und scheinen bei einem Neustart rückgängig gemacht zu werden.

Gegenmaßnahmen

Ich habe versucht, den Wert von "vm.max_map_count" mit dem 10-fachen des aktuellen Werts zu multiplizieren.

python


$ sysctl vm.max_map_count
vm.max_map_count = 65530
$ sudo sysctl -w vm.max_map_count=655300
vm.max_map_count = 655300

Ergebnis

Nach der Gegenmaßnahme habe ich den Code gestartet, um das Array des leeren Arrays erneut zu generieren.

python


$ node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => []); });'

Infolgedessen wurde der erwartungsgemäß angegebene Speicher von etwa 48 GB bis zum Ende verwendet und dann abnormal beendet.

<Ausgelassen>

48.66949462890625
48.756752014160156
48.79401397705078
48.831199645996094
48.86867141723633

<--- Last few GCs --->

<Ausgelassen>

<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x9fd5f0 node::Abort() [node]

<Ausgelassen>

11: 0x13cc8f9  [node]
Aborted (core dumped)

Nachwort

Immerhin war es kein knotenspezifisches Problem. Es war nicht einmal ein C ++ - Problem.

Anfänglich ist die Heap-Größe separat begrenzt, da sie bei "16,8 GB" endet. Ich frage mich, ob die Speichernutzung aufgrund von GC so ist und ob 2,2 Milliarden Objekte in C ++ neu sind, reicht eine 32-Bit-Ganzzahl aus Ich dachte, dass es verschwinden und in neuem versagen könnte.

Recommended Posts

Wenn der verfügbare Speicher des Knotens erhöht wird, kann er durch vm.max_map_count begrenzt werden
PyQtGraph ist möglicherweise nicht im Interpreter verfügbar.
Seien Sie vorsichtig, wenn Sie die Eigenvektoren einer Matrix unterscheiden
Bei Verwendung von tf.print () kann der Inhalt des Tensors nicht angezeigt werden, wenn er sich innerhalb der F-Zeichenfolge befindet.
Ermitteln Sie den Durchmesser des Diagramms anhand der Suche nach Breitenpriorität (Python-Speicher).
Goldnadel für den Fall, dass sie zu einem Stein wird, wenn man sich die Formel der Bildverarbeitung ansieht