Jak unikać wycieków pamięci w domknięciach JavaScript
Domknięcia w JavaScript przechowują obiekty w pamięci, dopóki istnieją odwołania do funkcji-domknięcia. Prowadzi to do wycieków pamięci, gdy obiekt nie jest już potrzebny, ale moduł zbierający śmieci nie może go usunąć z powodu przechwycenia w domknięciu.
Rozważmy typowy przypadek z dużym obiektem:
let bigData = {
id: 42,
data: new Array(1000000).fill('Jakiś dane')
}
function handler(data) {
return function() {
console.log(data.id);
debugger;
}
}
const runHandler = handler(bigData);
bigData = null;
runHandler();
Wewnętrzna funkcja używa tylko data.id, ale domknięcie przechwytuje cały obiekt bigData. W DevTools w punkcie przerwania widać, że obiekt pozostaje w pamięci.
Problemy z eval() w domknięciach
Funkcja eval() pogłębia wycieki pamięci, ponieważ silnik dodaje wszystkie zewnętrzne zmienne do domknięcia niezależnie od ich użycia:
let hello = 'Cześć';
let userName = 'Janek';
setTimeout(function () {
debugger;
return eval('console.log("Cześć, to Janek!")');
}, 1000);
W DevTools domknięcie zawiera hello i userName, chociaż eval() się do nich nie odwołuje. Dzieje się tak z powodu nieokreśloności kodu w eval(). Unikaj eval() w produkcji.
Metody zapobiegania wyciekom
Przymusowe resetowanie danych
Resetuj przechwycone dane po użyciu:
function runCalc(buttonId) {
let bigData = new Array(1000000).fill('Coś');
const button = document.getElementById(buttonId);
button.addEventListener('click', function onClick() {
console.log('Przetworzono elementy:', bigData.length);
bigData = null;
});
}
Przypisanie null zwalnia odwołanie wewnątrz domknięcia, pozwalając modułowi zbierającemu usunąć obiekt.
Poprawne usuwanie obsługi zdarzeń
Obsługa zdarzeń to częste źródło wycieków przy usuwaniu elementów DOM:
function clickSetup(btn, message) {
const bigData = new Array(1000000).fill(message);
function handler() {
console.log(message, bigData.length);
btn.remove();
btn.removeEventListener('click', handler);
}
btn.addEventListener('click', handler);
}
Nazwana funkcja handler pozwala na jej usunięcie wraz z elementem. Anonimowe funkcje strzałkowe nie mogą być usunięte.
WeakMap dla słabych odwołań
WeakMap rozwiązuje problem przechwytywania dużych obiektów, używając słabych odwołań do kluczy-obiektów.
Klucze tylko obiekty
const weak = new WeakMap();
const key = {};
weak.set(key, 'Tutaj był Janek!');
console.log(weak.get(key));
const anotherKey = {};
console.log(weak.get(anotherKey)); // undefined
Klucze to tylko obiekty lub nie-prymitywy. Dostęp przez get(key) według odwołania.
Słabe odwołania i zbieranie śmieci
Odwołania w WeakMap nie przeszkadzają w zbieraniu śmieci:
const weak = new WeakMap();
let userKey1 = {id: 1};
weak.set(userKey1, 'Janek');
userKey1 = null;
Po wyzerowaniu userKey1 obiekt i wartość są usuwane przez moduł zbierający.
Brak iteratorów
Brak size, forEach, keys(). Tylko podstawowe metody:
set(key, value)— ustawianieget(key)— pobieraniehas(key)— sprawdzaniedelete(key)— usuwanie
Iteratory nie istnieją, aby uniknąć nieprzewidywalności przy zbieraniu śmieci.
Praktyka z WeakMap w domknięciach
Domknięcie z WeakMap nie przechowuje obiektów:
function createProcessor() {
const weakMap = new WeakMap();
return function process(obj) {
weakMap.set(obj, obj.value);
debugger;
return weakMap.get(obj);
}
}
const processor = createProcessor();
let bigData = {
value: 21,
hugeArray: new Array(1000000).fill('*')
};
console.log(processor(bigData));
bigData = null;
let bigData2 = {
value: 42,
hugeArray: new Array(1000000).fill('1')
};
console.log(processor(bigData2));
W DevTools po zbieraniu śmieci pozostaje tylko bigData2. WeakMap pozwala automatycznie czyścić pamięć.
Do weryfikacji:
- Ustaw punkt przerwania w
process(). - Wykonuj kod krok po kroku (F10).
- Po
bigData = nullprzejdź do zakładki Memory. - Wywołaj zbieranie śmieci przyciskiem.
- Kontynuuj — zobaczysz tylko aktualne klucze.
Co jest ważne
- Domknięcia przechwytują cały obiekt, nawet jeśli używane jest jedno właściwość.
eval()dodaje wszystkie zewnętrzne zmienne do domknięcia.- Nazwane obsługi zdarzeń są usuwane przez
removeEventListener. WeakMapużywa słabych odwołań do kluczy-obiektów.- Brak iteratorów w
WeakMapdla przewidywalności zbierania śmieci.
— Editorial Team
Brak komentarzy.