Axlat yig'ish (Garbage collection)
JavaScript'da xotira boshqaruvi avtomatik va ko'zga ko'rinmas tarzda amalga oshiriladi. Biz o'zgaruvchilar, obyektlar, funksiyalar yaratamiz โ bularning hammasi xotira egallaydi. Endi savol: kerak bo'lmay qolgan narsalar bilan nima bo'ladi? Buni axlat yig'uvchi (garbage collector) hal qiladi.
Erishuvchanlik (Reachability)
JavaScript'dagi xotira boshqaruvining asosiy tushunchasi โ erishuvchanlik (reachability). Sodda qilib aytganda, "erishsa bo'ladigan" qiymatlar โ bu qandaydir yo'l bilan ishlatilishi mumkin bo'lgan qiymatlar. Ular xotirada saqlanib qoladi.
Erishsa bo'ladigan qiymatlar to'plami mavjud. Ba'zi qiymatlar ta'rifga ko'ra erishsa bo'ladigan hisoblanadi. Bularni root (ildiz) deb ataymiz:
- Hozir bajarilayotgan funksiyaning lokal o'zgaruvchilari va parametrlari;
- Ichma-ich chaqiruvlar zanjiridagi boshqa funksiyalarning o'zgaruvchi va parametrlari;
- Global o'zgaruvchilar;
- (va yana bir nechta ichki, tizim darajasidagilar).
Asosiy qoida shunday: agar biror qiymatga rootdan havola yoki havolalar zanjiri orqali yetib borilsa โ u erishsa bo'ladigan (kerakli) hisoblanadi va saqlanadi. Aks holda โ u "axlat" va tozalanadi.
Oddiy misol
Global o'zgaruvchida obyektga havola bor:
// user global o'zgaruvchisi obyektga havola qiladi
let user = {
name: "Jaloliddin"
};
Bu yerda user obyektga ("strelka") havola qiladi. Obyektning name xossasi primitivni saqlaydi. Agar user qiymatini boshqasiga o'zgartirsak, havola yo'qoladi:
user = null;
// Endi "Jaloliddin" obyektiga hech qanday havola yo'q.
// Unga erishib bo'lmaydi โ garbage collector uni o'chiradi,
// xotira bo'shatiladi.
Ikkita havola
Endi bir obyektga ikkita havola bo'lsa, vaziyat o'zgaradi:
let user = {
name: "Jaloliddin"
};
let admin = user; // ikkinchi havola
Agar endi userni null qilsak:
user = null;
Obyekt hamon erishsa bo'ladigan bo'lib qoladi, chunki unga admin orqali yetib borilyapti. U xotirada saqlanadi. Faqat admin = null ham qilsak, obyekt erishib bo'lmaydigan bo'ladi va o'chiriladi.
O'zaro bog'langan obyektlar
Endi murakkabroq misol โ bir-biriga havola qiluvchi obyektlar. Bir oila obyektini tasavvur qiling:
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
};
}
let family = marry(
{ name: "Sardor" },
{ name: "Nilufar" }
);
Natijada obyektlar bir-biriga havola qiladi: family ikkalasiga, father va mother esa husband/wife orqali o'zaro bog'langan. Barcha obyektlarga family (root)dan yetib boriladi โ hammasi erishsa bo'ladigan.
Endi ikkita havolani olib tashlaymiz:
delete family.father;
delete family.mother.husband;
"Sardor" (father) obyektiga endi hech qanday kiruvchi havola qolmadi: family.father o'chirildi, woman.husband ham. Uning ichidan tashqariga havolalar bo'lishi (masalan, u wifega havola qilishi) ahamiyatsiz โ muhimi unga tashqaridan yetib bo'lmasligi. Shu sabab "Sardor" obyekti o'chiriladi.
Yetib bo'lmaydigan orol
Bir butun bog'langan obyektlar guruhi ham axlatga aylanishi mumkin โ agar butun guruhga tashqaridan yetib bo'lmasa. Agar family = null qilsak, ichkarida obyektlar bir-biriga havola qilib tursa ham, ularga root'dan yo'l yo'q. Shunday "orol" butunligicha o'chiriladi.
Ichki algoritm: mark-and-sweep
Asosiy axlat yig'ish algoritmi "mark-and-sweep" (belgila va tozala) deb ataladi. U taxminan quyidagicha ishlaydi:
- Garbage collector root'larni oladi va ularni "belgilaydi" (eslab qoladi);
- So'ng root'lardan havola qilinayotgan barcha obyektlarga tashrif buyurib, ularni belgilaydi;
- So'ng belgilangan obyektlarga tashrif buyuradi va ular havola qilayotganlarni belgilaydi. Barcha yo'llar to'liq ko'rilguncha bu davom etadi;
- Belgilanmagan barcha obyektlar โ erishib bo'lmaydigan hisoblanadi va o'chiriladi (xotira bo'shatiladi).
Buni bo'yoq to'kilishiga o'xshatish mumkin: root'dan boshlab, havolalar bo'ylab "bo'yoq" tarqaladi. Bo'yoq yetib bormagan (belgilanmagan) obyektlar โ erishib bo'lmaydigan va o'chiriladi.
Optimallashtirishlar
Zamonaviy dvigatellar axlat yig'ishni tezlashtirish uchun ilg'or usullar qo'llaydi:
- Avloviy yig'ish (generational collection) โ obyektlar "yangi" va "eski"larga bo'linadi. Ko'p obyekt yaratilib, tez ishlatilib bo'ladi; ularni tez-tez tekshiradi. Uzoq yashaganlarini kamroq tekshiradi;
- Bosqichli yig'ish (incremental collection) โ juda ko'p obyekt bo'lsa, hammasini birdan belgilash kechikish yaratadi. Shuning uchun ish kichik bo'laklarga bo'lib bajariladi;
- Bo'sh vaqtda yig'ish (idle-time collection) โ protsessor bo'sh bo'lganda ishlab, asosiy kodga ta'sirni kamaytirishga harakat qiladi.
Xulosa
- Xotira boshqaruvi JavaScript'da avtomatik โ biz uni qo'lda boshqarmaymiz;
- Asosiy tushuncha โ erishuvchanlik (reachability): root'dan yetib boriladigan qiymatlar saqlanadi;
- Rootlar โ global o'zgaruvchilar, joriy funksiya o'zgaruvchilari va boshqalar;
- Obyektni saqlab qoladigan narsa โ unga kiruvchi havolalar, chiquvchilari emas;
- Bir-biriga bog'langan, ammo root'dan uzilgan obyektlar guruhi butunligicha o'chiriladi;
- Asosiy algoritm โ mark-and-sweep; dvigatellar buni turli optimallashtirishlar bilan tezlashtiradi.