Node.js похоже не освобождает память после цикла - PullRequest
1 голос
/ 15 февраля 2020
const axios = require('axios');
const JSDOM = require('jsdom').JSDOM;

axios.get('https://facebook.com')
    .then(response => {
        let i=1;
        while (true) {
            console.log(i++);
            const dom = new JSDOM(response.data);
            dom.window.close();
        }
    });

Вышеописанное будет выполняться (на моей машине) 440 раз, затем cra sh со следующим:

<--- Last few GCs --->

[13411:0x5dc70d0]    63566 ms: Mark-sweep 2042.3 (2051.1) -> 2041.3 (2051.1) MB, 2407.4 / 0.0 ms  (average mu = 0.123, current mu = 0.027) allocation failure scavenge might not succeed
[13411:0x5dc70d0]    67327 ms: Mark-sweep 2042.0 (2051.1) -> 2041.1 (2050.9) MB, 3745.2 / 0.0 ms  (average mu = 0.055, current mu = 0.004) allocation failure scavenge might not succeed


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x145cc79]
    1: StubFrame [pc: 0x145dab5]
Security context: 0x0d2e98240921 <JSObject>
    2: /* anonymous */ [0x374dcc798739] [/pathtomyproject/node_modules/parse5/lib/tokenizer/index.js:~644] [pc=0x323d7cd98d24](this=0x10b528b73279 <Tokenizer map = 0x242f38ec5c11>,95)
    3: getNextToken [0x374dcc799419] [/pathtomyproject/node_modules/parse5/lib/tokenizer/i...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

Writing Node.js report to file: report.20200215.163602.13411.0.001.json
Node.js report completed
 1: 0xa9d570 node::Abort() [node]
 2: 0xa9f832 node::OnFatalError(char const*, char const*) [node]
 3: 0xc0758e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
 4: 0xc07909 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
 5: 0xdb5e15  [node]
 6: 0xdb64a6 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
 7: 0xdc4d19 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
 8: 0xdc5b55 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 9: 0xdc862c v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
10: 0xd8f204 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]
11: 0x10dc52e v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
12: 0x145cc79  [node]
Aborted (core dumped)

Так что, я думаю, даже после переназначения dom на каждой итерации, предыдущие значения все еще хранятся в куче. Почему это и как мне это предотвратить?

1 Ответ

1 голос
/ 16 февраля 2020

Сборка мусора в V8 выполняется, когда ваш код не запущен и находится между действиями. Ваш while(true) l oop работает вечно, и, таким образом, вы никогда не дадите V8 замену для сборки мусора.

По многим дополнительным причинам вы никогда не будете выполнять вечно l oop в Javascript (если у вас нет await внутри l oop), потому что nodejs является в значительной степени однопоточной системой, управляемой событиями, и когда вы заняты включением while(true) l oop, никакие события не могут быть обработаны так много вещей не может быть сделано в nodejs.

Почему это и как я могу предотвратить это?

Сборщики мусора часто предназначены для код запускается и не мешает их выполнению, а затем, в тот момент, когда ваш код ничего не делает и завершил работу со своим внутренним состоянием, сборщик мусора проверяет состояние объектов в куче, чтобы найти объекты, которые больше не используются.

Вы предотвратите это, не входя в ограниченный l oop, как этот, не предоставляя времени простоя, чтобы G C мог работать. Как объяснено выше, жесткий l oop, как это, предотвращает запуск любых других событий в nodejs в любом случае, так что это вообще плохая причина и по другим причинам.

Обратите внимание, что ваш код не только создает 444 dom экземпляров и всех объектов, связанных с этими экземплярами, но он не дает возможности очистить все временные переменные / строки, которые использовались при создании этого экземпляра dom, что может быть важно при разборе HTML Таким образом, общее использование памяти значительно выше, чем у 444 законченных dom экземпляров Если вы вставите в l oop какой-либо тип передышки, то у G C появится возможность убрать вещи, которые больше не используются.

...