Пошук уроків, статей та іншого контенту
Як JavaScript вирішує, коли звільнити пам'ять, і які патерни коду призводять до витоків пам'яті.
На відміну від мов на кшталт C, у JavaScript не потрібно вручну звільняти пам'ять — цим займається збирач сміття (garbage collector, GC). Але «автоматично» не означає «неможливо припуститись помилки»: код, який ненавмисно тримає посилання на непотрібні об'єкти, змушує GC вважати їх усе ще потрібними — і пам'ять не звільняється, навіть автоматично. Це і називається витоком пам'яті (memory leak).
GC звільняє пам'ять об'єкта, коли до нього неможливо дістатись жодним ланцюжком посилань від «коренів» (глобальні змінні, поточний стек викликів). Поки існує хоча б один такий ланцюжок — об'єкт «досяжний» і лишається в пам'яті:
let user = { name: "Олена" }; // об'єкт досяжний через змінну user
user = null; // жодних посилань не лишилось — об'єкт стає кандидатом на звільненняsetInterval і addEventListener (модуль «DOM та браузер») утримують посилання на свій колбек, доки їх явно не скасувати — якщо колбек замикає (модуль «Функції») великий об'єкт, той теж лишається живим, поки таймер чи слухач не прибрано:
function startPolling(largeDataSet) {
const intervalId = setInterval(() => {
console.log(largeDataSet.length); // замикання утримує largeDataSet живим
}, 1000);
return () => clearInterval(intervalId); // функція очищення — обов'язково викликати, коли polling більше не потрібен
}Якщо видалити елемент зі сторінки (модуль «DOM та браузер»), але десь у коді (наприклад, у масиві чи звичайному Map) лишилось посилання саме на цей елемент, він не звільниться з пам'яті, попри те, що вже не видимий на сторінці:
const cachedElements = [];
const button = document.querySelector("#temp-btn");
cachedElements.push(button); // зберегли посилання
button.remove(); // видалили зі сторінки — але масив cachedElements усе ще тримає його живимСаме для цього сценарію в уроці про WeakMap/WeakSet ми розглядали слабкі посилання — якщо кеш елементів побудований на WeakMap/WeakSet замість звичайного масиву чи Map, видалений елемент звільниться автоматично.
Модуль «Функції» показав, що замикання утримує змінні зі свого оточення. Якщо функція випадково захоплює велику структуру даних, хоча реально користується лише одним полем, уся структура лишається в пам'яті, доки живе замикання:
function processReport(hugeReport) {
const summary = hugeReport.total; // потрібне лише одне поле
return function () {
console.log(summary); // якщо тут використати hugeReport.total напряму замість summary,
}; // замикання захопило б увесь hugeReport, а не одне число
}У DevTools (модуль «Вступ до JavaScript») вкладка Memory дозволяє зробити знімок (heap snapshot) стану пам'яті в різні моменти й порівняти — якщо кількість об'єктів певного типу постійно зростає після дій, які мали б бути «нейтральними» для пам'яті (наприклад, відкрити й закрити модальне вікно кілька разів), це ознака витоку.
clearInterval/clearTimeout, коли таймер більше не потрібен.
removeEventListener для обробників, доданих динамічно (особливо перед видаленням елемента, до якого їх прикріплено).
Використання WeakMap/WeakSet замість звичайних колекцій там, де життєвий цикл об'єктів-ключів контролює інший код.
Захоплення в замиканнях лише того, що справді потрібне, а не цілого великого об'єкта.
Забутий clearInterval при демонтажі компонента чи закритті частини інтерфейсу, що його запустила.
Зберігання посилань на DOM-елементи в довготривалих структурах (кеш, глобальний масив) без очищення після видалення елемента.
Припущення, що збирач сміття «якось сам розбереться» з будь-яким кодом — він розбирається лише з тим, що справді стало недосяжним.
Збирач сміття звільняє пам'ять об'єктів, недосяжних від коренів (глобальні змінні, стек викликів) — це автоматично, але не застраховує від коду, що ненавмисно тримає зайві посилання: забуті таймери, слухачі подій, «відірвані» DOM-вузли в довготривалих структурах, надто «жадібні» замикання. WeakMap/WeakSet з попереднього уроку — прямий інструмент для одного з цих сценаріїв. Цим завершується core-курс JavaScript цієї платформи — далі можна поглиблюватись в окремі технології: TypeScript, React, Node.js.
Спробуйте самостійно
Перегляньте функцію startPolling із цього уроку і поясніть своїми словами: що станеться з пам'яттю, якщо викликати startPolling(hugeData) і НІКОЛИ не викликати повернуту функцію очищення?