Подтвердить что ты не робот

Потребление памяти NodeJS в бесконечном цикле

Я не знаю, является ли это ошибкой с Node или V8, но если я запустил следующий код, процесс Node протекает в памяти. Кажется, что в GC не появляется, и через несколько секунд он потребляет > 1 ГБ памяти. Это неожиданное поведение. Я что-то пропустил?

Здесь код:

for(;;) { console.log(1+1); }

Очевидно, что это немного надуманная ситуация, но я вижу проблему с длительным процессом, который никогда не освобождает память.

Изменить: я пробовал как с v0.5.10 (неустойчивый), так и с v0.4.12 (стабильный), а нестабильная версия работает немного лучше - стабильная версия просто перестает выводить на консоль, но продолжает потреблять память, тогда как стабильная версия продолжает выполнять и потреблять память без паузы.

4b9b3361

Ответ 1

Вы блокируете цикл событий node.js, не возвращаясь к нему.

Когда вы пишете что-то в поток node.js делает это асинхронно: он отправляет запрос на запись, ставит в очередь информацию о отправленном запросе в потоковых внутренних структурах данных и ожидает обратного вызова, который уведомляет его о завершении.

Если вы блокируете цикл событий, обратный вызов никогда не будет вызываться (поскольку входящие события никогда не обрабатываются), а вспомогательные структуры данных, поставленные в очередь в потоке, никогда не будут освобождены.

То же самое может произойти, если вы "перегружаете" цикл событий, постоянно планируя собственные события с помощью nextTick/setInterval/setTimeout.

Ответ 2

Ответ @VyacheslavEgorov кажется правильным, но я бы предположил, что отсрочка цикла события решит проблему. Вы можете сравнить, как ваш бесконечный for-loop сравнивается с этой стратегией бесконечного цикла:

function loginf() {
  console.log(1+1);
  process.nextTick(loginf);
}
loginf();

Идея состоит в том, чтобы использовать process.nextTick(cb), чтобы отложить до цикла событий и (предположительно) позволить GC выполнять свою работу.

Ответ 3

Поскольку Node.js v0.10 был выпущен, setImmediate следует использовать в качестве первого выбора вместо process.nextTick при вызове рекурсивного обратного вызова.

function loginf() {
  console.log(1+1);
  setImmediate(loginf);
}
loginf();

Потребление памяти этого фрагмента кода оставалось низким (< 10 МБ) после работы в течение примерно 15 минут на моем компьютере.

Наоборот, запустив бесконечный for loop вызванный фрагмент памяти, а process.nextTick выбрал ошибку Maximum call stack size exceeded.

Проверьте этот Q & A также: setImmediate vs. nextTick