JIT — dastur kodi yoki bytecode’ni bajarilish paytida native machine code’ga aylantiradigan compilation usuli. Uning to‘liq nomi Just-In-Time Compilation hisoblanadi.
JIT interpreter tez startupi bilan oldindan compile qilingan native kod performance’i o‘rtasida muvozanat yaratadi. Runtime dastur qanday ishlayotganini kuzatib, eng ko‘p bajariladigan qismlarni optimallashtiradi.
Ishlash modeli
Oddiy jarayon:
- Dastur bytecode yoki oraliq kodda yuklanadi.
- Dastlab interpreter orqali bajarilishi mumkin.
- Runtime method va looplar necha marta ishlatilganini kuzatadi.
- “Issiq” kod JIT compilerga beriladi.
- Native code yaratiladi.
- Keyingi chaqiriqlar native versiyaga yo‘naltiriladi.
Bu tiered compilationning umumiy ko‘rinishi.
Hot code
Tez-tez bajariladigan function, method yoki loop hot code deb ataladi.
Runtime counter yoki sampling orqali uni aniqlaydi.
Har kodni darhol kuchli optimization bilan compile qilish qimmat. Sovuq kod interpreterda yoki tez, kam optimizatsiyali compilerda qolishi mumkin.
Tiered compilation
JIT bir nechta darajada ishlashi mumkin:
- interpreter;
- tez baseline compiler;
- o‘rta optimization;
- yuqori optimization.
Dastur uzoq ishlasa, muhim kod yuqori darajaga ko‘tariladi.
Bu startup va uzoq muddatli performance’ni muvozanatlashtiradi.
Runtime profiling
AOT compiler barcha real inputni oldindan bilmaydi.
JIT esa runtime’da quyidagilarni kuzatadi:
- object type;
- branch qaysi tomonga ko‘p ketadi;
- method target;
- loop count;
- allocation;
- exception;
- call frequency.
Bu ma’lumot asosida spekulyativ optimization bajariladi.
Inlining
Method chaqiruvi o‘rniga uning kodi caller ichiga qo‘yiladi.
Afzalliklari:
- call overhead kamayadi;
- keyingi constant propagation;
- dead code elimination;
- type specialization
imkoniyati paydo bo‘ladi.
Juda ko‘p inlining code size va instruction cache bosimini oshiradi.
Devirtualization
Virtual method chaqirig‘i runtime profilingda doim bitta type’ga tegishli bo‘lsa, JIT uni to‘g‘ridan-to‘g‘ri chaqiriqqa aylantirishi mumkin.
Bu inliningni ham ochadi.
Agar keyin boshqa type paydo bo‘lsa, spekulyatsiya bekor qilinadi.
Deoptimization
JIT taxmin asosida optimizatsiya qiladi.
Masalan, variable doim integer bo‘ladi deb hisoblaydi. Keyin boshqa type keladi.
- optimizatsiyalangan koddan chiqadi;
- interpreter yoki kam optimizatsiyali kodga qaytadi;
- state’ni tiklaydi;
- kerak bo‘lsa qayta compile qiladi.
Bu deoptimization deb ataladi.
On-stack replacement
Uzoq loop interpreterda boshlangan bo‘lishi mumkin. Loop tugashini kutmasdan JIT bajarilishni optimizatsiyalangan kodga ko‘chiradi.
Bu On-Stack Replacement deb ataladi.
Benchmark warm-up jarayonida muhim.
Code cache
JIT hosil qilgan machine code maxsus xotira hududida saqlanadi.
Code cache:
- hajm limiti;
- executable permission;
- garbage collection;
- invalidation;
- profiling metadata
ga ega.
Cache to‘lsa yangi methodlar JIT qilinmasligi yoki eski kod chiqarilishi mumkin.
W^X
Xavfsizlik uchun memory page bir vaqtda writable va executable bo‘lmasligi kerak.
JIT avval sahifaga kod yozadi, keyin permissionni executable qiladi.
Ba’zi platforma signed JIT, entitlement yoki maxsus API talab qiladi.
Startup xarajati
JIT compilerning o‘zi CPU va memory ishlatadi.
Dastlab:
xarajati mavjud.
Qisqa umrli CLI dasturda AOT yoki interpreter tezroq umumiy natija berishi mumkin.
Warm-up
Dastur boshlanganda hali hot code aniqlanmagan va optimized native code yo‘q.
Bir necha takrorlashdan keyin performance yaxshilanadi.
Benchmark JIT warm-upni hisobga olmasa noto‘g‘ri xulosa beradi.
Pause
Ba’zi JIT compilation application thread’da, boshqasi background compiler threadda bajariladi.
Compilation CPU contention yaratishi mumkin.
Latency-sensitive tizimda compiler threadlari va optimization darajasi nazorat qilinadi.
JIT va garbage collection
JIT object layout, write barrier va safepointlarni bilishi kerak.
GC objectlarni ko‘chirsa, compiled code reference’lari yangilanadi yoki handle orqali ishlaydi.
JIT stack map yaratib, qaysi register va stack slot object reference ekanini ko‘rsatadi.
Safepoint
Runtime thread holatini aniq ko‘ra oladigan nuqta safepoint.
GC, deoptimization yoki profiling uchun threadlar safepointga kelishi mumkin.
Uzoq loop safepoint tekshiruvisiz bo‘lsa, stop-the-world kechikadi.
JIT va AOT
| Jihat | JIT | AOT |
|---|---|---|
| Compile vaqti | Runtime | Build yoki install |
| Profil ma’lumoti | Haqiqiy workload | Oldindan yoki profil fayli |
| Startup | Compilation xarajati bor | Odatda tezroq |
| Peak performance | Yuqori bo‘lishi mumkin | Barqaror |
| Code portability | Bytecode tarqatish mumkin | Targetga xos artifact |
| Runtime murakkabligi | Yuqori | Kamroq |
Ko‘p platforma JIT va AOTni birga ishlatadi.
Profile-guided optimization
Oldingi ishga tushishdan profiling ma’lumoti yig‘ilib, keyingi compile’da ishlatilishi mumkin.
Bu AOTga JITga o‘xshash real workload ma’lumotini beradi.
Dynamic holat o‘zgarsa profil eskirishi mumkin.
Diagnostika
JIT muammosida:
- compilation log;
- hot method;
- deoptimization soni;
- code cache;
- compiler thread CPU;
- warm-up;
- runtime flag;
- generated assembly;
- GC safepoint;
- platform permission
tekshiriladi.
Peak performance sekin bo‘lsa faqat source code emas, JIT qanday machine code yaratgani ham ko‘riladi.
Inline cache
Dynamic method yoki property lookup natijasi call site yonida cache qilinishi mumkin. Monomorphic inline cache bitta type’ni, polymorphic cache bir nechta type’ni eslab qoladi. Juda ko‘p turli type keladigan megamorphic joyda cache samarasi pasayadi va runtime umumiy lookup yo‘liga qaytadi.
Bog‘liq tushunchalar
Just-In-Time Compilation, Bytecode, Interpreter, AOT, Runtime profiling, Inlining, Deoptimization, Code cache, Safepoint, Virtual machine