Ответ 1
Это не то, что сборщик мусора работает не оптимально, но что он вообще не работает - вы не даете ему никаких шансов.
При разработке tco module, который tail call оптимизация в Node я заметил странную вещь. Казалось, это утечка памяти, и я не знал почему. Оказалось, что из-за немногих console.log()
звонки в разных местах, которые я использовал для тестирования, чтобы увидеть, что происходит, потому что, увидев результат рекурсивного вызова, уровень миллионов в миллионных глубинах занял некоторое время, поэтому я хотел увидеть что-то, пока он это делал.
Ваш пример очень похож на этот.
Помните, что Node является однопоточным. Когда ваши вычисления выполняются, ничто другое не может - включая GC. Ваш код полностью синхронный и блокирующий - хотя он генерирует миллионы promises блокирующим образом. Он блокируется, потому что он никогда не достигает цикла события.
Рассмотрим следующий пример:
var a = 0, b = 10000000;
function numbers() {
while (a < b) {
console.log("Number " + a++);
}
}
numbers();
Это довольно просто - вы хотите напечатать 10 миллионов номеров. Но когда вы запускаете его, он ведет себя очень странно - например, он печатает числа до некоторой точки, а затем останавливается на несколько секунд, затем он продолжает идти или может начать сбой, если вы используете swap, или, может быть, дает вам эту ошибку, Я только что получил номер 8486:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Aborted
Что здесь происходит, так это то, что основной поток блокируется в синхронном цикле, где он продолжает создавать объекты, но GC не имеет возможности их выпуска.
Для таких длительных задач вам нужно разделить работу и время от времени входить в цикл событий.
Вот как вы можете исправить эту проблему:
var a = 0, b = 10000000;
function numbers() {
var i = 0;
while (a < b && i++ < 100) {
console.log("Number " + a++);
}
if (a < b) setImmediate(numbers);
}
numbers();
Он делает то же самое - он печатает числа от a
до b
, но в пучках по 100, а затем он планирует себя продолжать в конце цикла событий.
Вывод $(which time) -v node numbers1.js 2>&1 | egrep 'Maximum resident|FATAL'
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Maximum resident set size (kbytes): 1495968
Он использовал 1,5 ГБ памяти и разбился.
Вывод $(which time) -v node numbers2.js 2>&1 | egrep 'Maximum resident|FATAL'
Maximum resident set size (kbytes): 56404
Он использовал 56 МБ памяти и закончил.
См. также эти ответы: