В двух словах. Если у вас есть строка на 20мб, а вы вырезали из нее подстроку на 13 или больше символов, то v8 будет хранить ссылку на изначальну большую строку и не будет вычищать память.
Пример кода со статьи:
function MemoryLeak() {
// 15МБ строка
let huge = "x".repeat(15*1024*1024);
// Маленький кусочек
let small = huge.substr(0,25);
return small;
}
let arr = [];
setInterval(e=>{
let str = MemoryLeak();
arr.push(str);
console.log(JSON.stringify(arr).length, 'байт');
}, 500);
Я проверил на Node v12.2.0Размер массива arr у меня был 3837 байт, а размер процесса дорос до 2ГБ и крашнулся с ошибкой
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
Такое поведение существует много лет, тикет был заведен еще в 2013 году.Насколько это большая проблема? Если мы возьмем бэкенд, то он обычно не хранит состояние в памяти между запросами, и если пропадет ссылка на arr, то сборщик мусора все вычистит. Но вы можете столкнуться с данной проблемой, когда у вас есть кеши, которые живут между запросами, также на фронтенде, когда у вас есть долгоживущее состояние приложения (в SPA).