Pərdəarxası JavaScript: V8 Mühərriki, İcra Konteksti (Execution Context) və Yaddaşın Anatomiyası
Təməl sintaksisdən kənara çıxaraq JavaScript-in daxili arxitekturasını araşdırırıq: Kodun parse olunmasından Call Stack və Memory Heap idarəçiliyinə qədər bütün icra zənciri.
Elgun Mammadli
Front End Developer

Əksər proqramçılar JavaScript ilə tanışlığa console.log("Hello World"), dəyişən təyinatları və ya funksiyalarla başlayır. Lakin dili dərindən anlamaq və Senior səviyyəsində sistem mühəndisliyi düşüncəsinə yiyələnmək üçün sintaksisin arxasında baş verən mühəndislik proseslərinə enmək vacibdir: Yazdığımız kod maşın üçün nə ifadə edir və JavaScript mühərriki onu kompüterin aparat təminatında (hardware) necə icra edir?
Bu məqalədə JavaScript-in daxili arxitekturasını, V8 mühərrikinin optimallaşdırma mexanizmlərini, İcra Kontekstinin (Execution Context) həyat dövrünü və yaddaş idarəetməsini araşdırırıq.
1. Mühərrikin Daxili Dünyası: Koddan Maşın Dilinə
JavaScript interpretasiya olunan deyil, müasir dövrdə JIT (Just-In-Time) Compilation ilə çalışan dildir. Dünyada ən məşhur JS mühərriki Google-un V8 mühərrikidir (Chrome, Node.js və Deno-da istifadə olunur).
Kodu brauzerə və ya Node.js mühitinə verdikdə proses aşağıdakı mərhələlərdən keçir:
Parser və AST (Mücərrəd Sintaksis Ağacı)
Mühərrik ilk növbədə leksik analiz aparır: kodu simvol-simvol oxuyaraq tokenlərə bölür. Daha sonra bu tokenlər əsasında sintaktik struktur — AST qurulur. AST kodun qrammatik iyerarxiyasını təmsil edən ağacvari məlumat strukturudur.
Ignition (İnterpreter)
V8-in Ignition adlı komponenti AST-ni oxuyur və onu platformadan asılı olmayan kompakt Bytecode-a çevirir. Bytecode maşın dilindən daha mücərrəddir, lakin dərhal icra edilə bilir. Bu, tətbiqin sürətlə start götürməsini təmin edir.
TurboFan və Profiler (JIT Optimallaşdırması)
Kod icra olunduqca mühərrikin daxilindəki Profiler sistemi hansı funksiyaların tez-tez çağırıldığını izləyir. Bu hissələr "Hot Code" adlanır. Profiler eyni zamanda arqumentlərin tiplərini (Type Feedback) qeydə alır.
Əgər funksiya davamlı olaraq eyni tipli arqumentlər qəbul edirsə, V8-in optimallaşdırıcı kompilyatoru olan TurboFan işə düşür və həmin Bytecode-u birbaşa CPU-nun anlayacağı maşın koduna (Machine Code) çevirir.
Deoptimallaşma (Deoptimization) Tələsi
JavaScript dinamik tiplidir. Əgər TurboFan bir funksiyanı rəqəmlər (number) üçün optimallaşdırıbsa və sonradan həmin funksiyaya mətn (string) ötürülərsə:
Mühərrik optimallaşdırılmış kodu ləğv edir (bailout) və yenidən yavaş Bytecode icrasına qayıdır. Monomorfik (sabit strukturlu) kod yazmağın performans baxımından əsas səbəbi məhz budur.
2. İcra Konteksti (Execution Context) Nədir?
Kod icra olunmağa başlayan andan etibarən JavaScript hər şeyi xüsusi mühitlərin — Execution Context (EC) daxilində idarə edir. Sadə dillə desək, Execution Context hazırda işləyən kodun "çalışma qutusu"dur.
İki əsas növü var:
- Global Execution Context (GEC): Kod işə düşdüyü an avtomatik yaranır. Tək bir qlobal kontekst mövcuddur. Brauzerdə
window, Node.js-dəglobalobyektini təyin edir. - Function Execution Context (FEC): Hər dəfə bir funksiya çağırıldıqda həmin funksiya üçün ayrıca icra konteksti yaradılır.
Hər bir kontekst iki əsas fazadan keçir:
Faza 1: Creation Phase (Yaradılma Fazası)
Bu fazada kod hələ sətirbəsətir işlədilmir. Mühərrik kodu skan edir və yaddaş sahəsi ayırır:
- Qlobal obyekt (
windowvə yaglobal) təyin edilir. thisdəyişəni kontekstə bağlanır.- Lexical Environment və Variable Environment qurulur.
- Dəyişənlər və funksiyalar üçün yaddaş ayrılır (Hoisting məhz burada baş verir).
Faza 2: Execution Phase (İcra Fazası)
Kod yuxarıdan aşağıya doğru sətirbəsətir icra olunur, dəyişənlərə real dəyərlər mənimsədilir və funksiyalar işə düşür.
3. Hoisting və Temporal Dead Zone (TDZ)-un Texniki Səbəbi
Dəyişənlərin yuxarı "qaldırılması" (hoisting) fiziki kod yerdəyişməsi deyil; bu, Creation Phase zamanı baş verən yaddaş qeydiyyatıdır.
Fərq mühərrikin dəyişən bəyannamələrini necə qeydiyyata almasındadır:
varaçar sözü: Yaradılma fazasında yaddaşa yazılır və ona dərhal ilkin dəyər olaraqundefinedmənimsədilir. İcra fazasında təyinata çatmamış müraciət etdikdəundefinedqaytarır.letvəconstaçar sözləri: Yaradılma fazasında yenə yaddaşda qeydiyyata alınır, lakin onlara heç bir ilkin dəyər təyin edilmir. Dəyişənin yaradıldığı an ilə kodda ona faktiki dəyər mənimsədildiyi sətir arasındakı zonaya Temporal Dead Zone (TDZ) deyilir. Bu aralıqda dəyişənə müraciətReferenceErrorxətası törədir.
4. Call Stack və Yaddaş Memarlığı: Stack vs Heap
JavaScript tək axınlı (single-threaded) dildir — yəni eyni anda yalnız bir əməliyyatı icra edə bilər. İcranın növbəliliyini Call Stack, məlumatların saxlanmasını isə Memory Heap idarə edir.
| Xüsusiyyət | Call Stack (Yığın) | Memory Heap (Topa) |
|---|---|---|
| Məqsəd | İcra axını, primitiv tiplər və ünvan göstəriciləri (pointers) | Mürəkkəb obyektlər, massivlər, funksiyalar |
| Quruluş | LIFO (Last In, First Out) strukturunda nizamlı yaddaş | Qeyri-müntəzəm, dinamik böyüyə bilən geniş yaddaş sahəsi |
| Sürət | Həddindən artıq sürətli çıxış | Göstərici ilə müraciət edildiyi üçün nisbətən yavaş |
Stack-də yalnız Heap-dəki obyektin yaddaş ünvanı (0x0012FA) saxlanılır. İki dəyişəni bir-birinə bərabər etdikdə obyektin nüsxəsi deyil, Stack-dəki həmin ünvan köçürülür:
Call Stack və Stack Overflow
Hər bir funksiya çağırışı Call Stack-ə yeni bir çərçivə (Stack Frame) əlavə edir. Funksiya bitdikdə həmin çərçivə stack-dən atılır (pop). Sonsuz rekursiya baş verdikdə stack-in limiti aşılır və Maximum call stack size exceeded xətası yaranır.
5. Garbage Collection: Mark-and-Sweep
Heap-də dinamik yerləşən məlumatlar daim orada qala bilməz. Mühərrikin Garbage Collector (GC) komponenti artıq istifadə olunmayan yaddaşı təmizləyir.
Müasir mühərriklər Mark-and-Sweep (İşarələ və Süpür) alqoritmindən istifadə edir:
- Kök elementlər (Roots): Qlobal dəyişənlər, hazırda aktiv Call Stack-dəki lokal dəyişənlər kök sayılır.
- Mark (İşarələmə): GC köklərdən başlayaraq onların istinad etdiyi bütün obyektləri "əlçatan" (reachable) kimi işarələyir. Sonra həmin obyektlərin istinad etdiyi alt obyektləri də zəncirvari şəkildə tapır.
- Sweep (Süpürmə): Heç bir kökdən əlçatan olmayan (işarələnməmiş) obyektlərin yaddaşı azad edilir.
Yaddaş Sızması (Memory Leak) Təhlükəsi
Əgər obyektə artıq ehtiyac yoxdursa, lakin təsadüfən qlobal bir massivdə, silinməmiş hadisə dinləyicisində (addEventListener) və ya qapalı qalan closure-da referansı qalıbsa, GC onu silə bilmir. Nəticədə tətbiqin yaddaş sərfi getdikcə artır.
Məqaləni paylaş
Pərdəarxası JavaScript: V8 Mühərriki, İcra Konteksti (Execution Context) və Yaddaşın Anatomiyası