Zarządzanie pamięcią

Garbage Collector

Podczas pracy nad kodem non stop używamy wszelakich danych, które muszą być przetrzymywane w pamięci.

W Javascript podobnie do wielu języków wysokopoziomowych stosuje się automatyczne zarządzanie pamięcią czyli mechanizm zwany Garbage Collection. Oznacza to, że gdy tworzymy nowe obiekty, Javascript automatycznie przydziela im pamięć, a gdy nie są już przez nas używane, są one z niej automatycznie usuwane.

Przydzielanie pamięci nie jest raczej rzeczą trudną (...), ale jak sprawdzić co możemy w danej chwili usunąć by nie zepsuć działania programu?

Do takiego sprawdzania wykorzystywane są różne algorytmyarrow-up-right, które opierają swoje działanie na pojęciu "dostępności obiektów". Oznacza ono, że jeżeli do danego obiektu nie można się w żaden sposób odwołać (nie ma do niego żadnej referencji), staje się on nieużywalny, a więc i może być usunięty z pamięci.

W dzisiejszych czasach (a w zasadzie gdzieś od 2012 roku) w Javascript wykorzystywany jest algorytm zwany "mark-and-sweep".

Algorytm ten zakłada istnienie głównych obiektów (roots). Dla języka Javascript takim obiektem jest główny obiektarrow-up-right (dla przeglądarki Window).

Garbage Collector cyklicznie co jakiś czas rozpoczyna swoją pracę od głównego obiektu. Oznacza go jako obiekt z referencją (mark). Następnie poprzez jego odwołania przechodzi do innych obiektów - je także oznaczając. Następnie GC sprawdza te obiekty i ich odwołania do kolejnych obiektów, oznacza je i tak dalej, aż dojdzie do końca. Po oznaczeniu wszystkich obiektów do których prowadzą jakiekolwiek referencje, GC zaczyna usuwać obiekty nieużywalne, do których nie było żadnych odwołań.

W realnym świecie powyższe działanie jest o wiele bardziej skomplikowane, ponieważ przez ostatnie lata zostało wprowadzone do silnika Javascript wiele usprawnień, które powodują, że cały proces jest szybszy i nie blokuje działania skryptów.

Tak naprawdę na co dzień nie musisz znać bardzo dokładnie wszystkich mechanizmów jakie działają w tle - tym bardziej, że przeglądarki się zmieniają i wprowadzają tutaj swoje własne rozwiązania.

Jeżeli temat cię zainteresował, polecam zapoznać się z artykułami: https://blog.sessionstack.com/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks-3f28b94cfbecarrow-up-right i https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Managementarrow-up-right

Przykłady działania

Jak to wygląda od strony kodu? Stwórzmy przykładowo prostą zmienną z obiektem:

Jeżeli teraz pod zmienną ob podstawimy coś innego np.:

to obiekt na który wskazywała poprzednio nasza zmienna nie będzie już w żaden sposób dla nas dostępny i Garbage Collector usunie go z pamięci.

Jeżeli jednak istniała będzie jakakolwiek referencja do danego obiektu, Garbage Collector nie usunie go z pamięci.

W powyższym kodzie ustawiliśmy ob na null, ale zmienna ob2 dalej wskazuje na obiekt będący w pamięci. Z tego też powodu nie zostanie on usunięty przez Garbage Collector.

Podobna sytuacja może pojawić się jeszcze w kilku momentach - np. gdy dany obiekt wstawimy do obiektu typu Map, czy tablicy:

Znowu - ustawienie ob na null nic w tym przypadku nie da, ponieważ dalej możemy odwołać się do danego obiektu poprzez tablicę czyli zapis tab[0].

Kolejny przykład, gdzie GC nie będzie mógł wykonać swojego zadania pokazuje poniższy kod:

Czy widzisz tutaj błędy?

Niestety w żadnym z trzech przypadków GC nie będzie mógł usunąć zmiennych z pamięci. Wynika to z faktu, że przy tworzeniu zmiennych myName, i i el z własnej wygody (a może i lenistwa) nie użyliśmy słów kluczowych let/const. A przecież umówiliśmy się, że będziemy tak robić... Takie działanie spowodowało, że za każdym razem dostajemy zmienną globalną, która przypięta jest do obiektu Window, a więc nie będzie mogła być usunięta.

Warto tutaj zaznaczyć, że poprawa działania GC jest jednym z powodów wprowadzanie do Javascript let/const. Dzięki nim oraz odpowiedniemu używaniu bloków automatyczne oczyszczanie pamięci w większości przypadków będzie dla nas wystarczającym rozwiązaniem:

Last updated