Ступеньки без отката это почти всегда накопление, течь в самом движке тут ни при чём.
Три места, где в Node оно вылезает чаще всего.
Первое: результат копится в массиве и пишется в файл одним куском в конце. Пока прогон идёт, весь разобранный csv лежит в памяти. На 400 тысячах строк по 300 байт это уже больше сотни мегабайт, и сверху накладные расходы на объекты, а они у V8 немаленькие.
Второе: буферы ответов. Если вы собираете тело через body += chunk, каждая склейка порождает новую строку. Сборщик их подберёт, но с задержкой, и пик по памяти вы уже увидели.
Третье: очередь заданий целиком в памяти. Прочитали файл со списком через readFileSync и split, получили массив на 400 тысяч строк. Он живёт до конца прогона, потому что на него ссылается цикл.
У Scrapy память ниже ровно потому, что очередь по умолчанию уезжает на диск, а строки отдаются генератором по одной.