Потребление памяти NodeJS в бесконечном цикле
Я не знаю, является ли это ошибкой с Node или V8, но если я запустил следующий код, процесс Node протекает в памяти. Кажется, что в GC не появляется, и через несколько секунд он потребляет > 1 ГБ памяти. Это неожиданное поведение. Я что-то пропустил?
Здесь код:
for(;;) { console.log(1+1); }
Очевидно, что это немного надуманная ситуация, но я вижу проблему с длительным процессом, который никогда не освобождает память.
Изменить: я пробовал как с v0.5.10 (неустойчивый), так и с v0.4.12 (стабильный), а нестабильная версия работает немного лучше - стабильная версия просто перестает выводить на консоль, но продолжает потреблять память, тогда как стабильная версия продолжает выполнять и потреблять память без паузы.
Ответы
Ответ 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