Значительное увеличение использования памяти при итерации Promises в асинхронной функции - PullRequest
1 голос
/ 19 марта 2019

Я пытаюсь выяснить, что вызвало значительное увеличение использования памяти во время итераций в следующем коде.

async function a () {
    for (let i = 0; i < 10000000000; i++) {
        await new Promise(resolve => {
            if (i%100000 === 0) {
                console.log(i)
                console.log(process.memoryUsage())
            }
            resolve(i)
        })
    }
}
a()

Когда код выполняется, этот скачок использования памяти происходит несколько раз и всегда происходит, когда i достигает определенных чисел.

В 7.9.0 всегда происходит от 2000000 -> 2100000

1700000
{ rss: 20135936, heapTotal: 9355264, heapUsed: 6003256, external: 8772 }
1800000
{ rss: 19836928, heapTotal: 9355264, heapUsed: 4490432, external: 8772 }
1900000
{ rss: 19316736, heapTotal: 9355264, heapUsed: 5039992, external: 8772 }
<-- Jump happens between here -->
2000000
{ rss: 19357696, heapTotal: 9355264, heapUsed: 5587808, external: 8772 }
2100000
{ rss: 23605248, heapTotal: 13549568, heapUsed: 6088208, external: 8772 }
<-- and here -->
2200000
{ rss: 23601152, heapTotal: 13549568, heapUsed: 6586000, external: 8772 }
2300000
{ rss: 23568384, heapTotal: 13549568, heapUsed: 7083112, external: 8772 }

В 8.3.0 всегда происходит от 2600000 -> 2700000

2400000
{ rss: 30507008, heapTotal: 9437184, heapUsed: 4785896, external: 8252 }
2500000
{ rss: 30523392, heapTotal: 9437184, heapUsed: 4710912, external: 8252 }
<-- Jump happens between here -->
2600000
{ rss: 30539776, heapTotal: 9437184, heapUsed: 4636176, external: 8252 }
2700000
{ rss: 34742272, heapTotal: 13631488, heapUsed: 6606512, external: 8252 }
<-- and here -->
2800000
{ rss: 34750464, heapTotal: 13631488, heapUsed: 8571208, external: 8252 }
2900000
{ rss: 34758656, heapTotal: 13631488, heapUsed: 6412304, external: 8252 }

1 Ответ

2 голосов
/ 23 марта 2019

В V8 (среда выполнения JS, которую использует Node.js) для кучи предварительно выделяется установленный размер.Это heapTotal вы видите.Когда V8 подозревает, что вам понадобится больше места, он увеличит общий размер кучи.

С вашим примером кода получается, что в куче выделяется много небольших объектов.Это отражено в heapUsed и является фактическим объемом памяти, который используется вашим кодом.Когда куча заполняется, выполняется сбор мусора (GC), освобождая место.Так что, если вы построите график heapUsed при увеличении i, вы увидите, что он поднимается и поднимается до тех пор, пока не сработает GC, и он не упадет обратно.Я сделал для очень длинного пробега!

Heap usage for 10 billion iterations

Вы можете ясно видеть, что куча никогда не позволяла получить такую ​​большую раньшеGC включается.

Для дальнейшей проверки этого мы можем вручную запустить GC в нашем коде, если мы запустим следующее, используя node --expose_gc

async function run() {
    for (let i = 0; i < 10000000000; i++) {
        await new Promise(async resolve => {
            if (i % 10000000 === 0) {
                global.gc();
                console.log(`${i}, ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}mb`);
            }
            resolve();
        });
    }
}
run();

Из этоговы получите следующий вывод для v7.9.0

0, 3.12mb
10000000, 2.77mb
20000000, 2.78mb
30000000, 2.78mb
40000000, 2.78mb
50000000, 2.78mb
60000000, 2.78mb

Различные версии узлов

Что очень интересно, если мы запустим тест на разных версиях узла!

heap size plotted against number of iterations for different node versions

Как вы можете видеть, существует огромное различие в профиле памяти для pre-v8.2.0 и последующих версий node.js.Если мы посмотрим на журнал изменений для v8.3.0, то увидим, почему!

Движок V8 был обновлен до версии 6.0, в которой значительно изменился профиль производительности.

Это версия V8, включающая Turbofan , которая реализует море узлов и включала множество улучшений производительности для GC.

Более подробное представление о том, как работает V8 GC, доступно в репозитории v8-perf Торстена Лоренца.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...