JIT (Just-In-Time) — tehnologia de compilare dinamică care transformă bytecodul sau reprezentarea intermediară a programului în instrucțiuni mașină direct în timpul executării. În Android, compilatorul JIT a apărut pentru prima dată în versiunea 2.2 Froyo în cadrul mașinii virtuale Dalvik și a accelerat executarea aplicațiilor de 2–5 ori. Conform datelor Google, 2024, JIT modern în ART combină interpretarea cu compilarea profilată a metodelor hot.
Principalele aspecte
Just-In-Time (JIT) — metodă de compilare în care codul sursă sau bytecodul este transformat în instrucțiuni mașină nu dinainte (ca în AOT), ci în momentul primei apelări a porțiunii corespunzătoare a programului. Termenul „Just-In-Time” înseamnă că compilarea are loc „la fix” — imediat înainte de execuție.
Conceptul JIT există încă din anii 1960, dar răspândirea largă a avut loc odată cu apariția Java Virtual Machine în 1995. JIT permite combinarea portabilității bytecodului (scriem o dată — rulăm oriunde) cu performanța apropiată de codul nativ. În Java HotSpot VM, compilatorul JIT analizează codul executat și compilează doar cele mai critice porțiuni, economisind timp și memorie.
Compilatorul JIT primește la intrare bytecodul, îl interpretează și în paralel colectează statistici. Când o anumită porțiune de cod (metodă, buclă) este apelată suficient de des, JIT decide să o compileze. Codul mașină compilat este stocat în cache — la apelări repetate se folosește versiunea deja pregătită. Aceasta asigură accelerarea fără a fi nevoie să se compileze întregul program.
// Exemplu: metoda va deveni hot după apeluri repetate
public class HotMethod {
private int compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i * i;
}
return sum;
}
}
// Apelarea de 500 de ori în buclă — JIT va compila compute
for (int t = 0; t < 500; t++) {
hot.compute(1000);
}
În Android, compilarea JIT a parcurs trei faze de evoluție. Prima fază — Dalvik fără JIT (Android 1.0–2.1): interpretarea pură a bytecodului DEX. A doua fază — Dalvik cu JIT (Android 2.2–4.4): apariția compilatorului JIT, care a accelerat aplicațiile de 2–5 ori. A treia fază — ART cu JIT hibrid (Android 7.0+): revenirea JIT într-o calitate nouă.
JIT în Dalvik a fost implementat ca un compilator trace-based. Acesta nu analiza metode individuale, ci lanțuri de instrucțiuni (traces) care sunt executate frecvent secvențial. Acest lucru permitea compilarea unor trasee complete de execuție, inclusiv mai multe metode. O astfel de abordare era eficientă pentru procesoarele mobile cu cache de instrucțiuni mic, deoarece trace-ul compilat se încăpea în cache-ul L1.
Începând cu Android 7.0 Nougat, ART utilizează method-based JIT — compilează metode individuale pe baza profilurilor de execuție. Acest JIT funcționează semnificativ mai rapid decât Dalvik JIT: timpul tipic de compilare a unei metode este de 0.5–1 ms față de 3–5 ms în Dalvik. Codul compilat este stocat într-o zonă de memorie separată (JIT code cache), nu în heap-ul aplicației, ceea ce reduce fragmentarea.
| Parametru | Dalvik JIT | ART JIT |
|---|---|---|
| Tip | Trace-based | Method-based |
| Viteza de compilare | 3–5 ms/metodă | 0.5–1 ms/metodă |
| Pragul de compilare | ~200 de apeluri | Dinamic |
| Cache de cod | În heap-ul aplicației | JIT code cache |
| Profilare | Internă | Fișiere .prof externe |
Mecanismul central al JIT — detectarea metodelor hot. Fiecare apel de metodă incrementează un contor intern. Când contorul depășește pragul, metoda este marcată ca „fierbinte” și trimisă la compilare. În Dalvik, pragul era stabilit fix (~200 de apeluri). În ART, contoarele sunt ajustate dinamic în funcție de resursele disponibile ale dispozitivului.
Procesul de compilare include mai multe faze. Prima — analiza bytecodului: JIT studiază fluxul de instrucțiuni și construiește un graf al fluxului de date (data-flow graph). A doua — optimizarea: inline-ul metodelor mici, eliminarea codului mort, plierea constantelor. A treia — generarea codului: transformarea grafului optimizat în instrucțiuni mașină pentru arhitectura specifică a CPU (ARM, ARM64, x86).
// Demonstrarea inlining-ului — JIT va înlocui corpul metodei
public int inlineExample() {
return square(5);
}
private int square(int x) {
return x * x;
} // JIT va înlocui apelul cu return 5 * 5;
O tehnică specială a JIT — On-Stack Replacement (OSR). Dacă o metodă conține o buclă lungă care nu se termină timp de sute de iterații, JIT poate compila bucla „din zbor” și înlocui versiunea interpretată cu cea compilată direct în timpul execuției. OSR este deosebit de eficient pentru sarcinile de calcul: randare, procesare de imagini, criptografie.
JIT și AOT — două abordări ale compilării cu compromisuri opuse. JIT sacrifică viteza primei lansări pentru compactitatea distribuției și adaptabilitate. AOT sacrifică timpul de instalare și spațiul pe disc pentru performanță maximă din prima secundă. Niciuna dintre abordări nu este absolut mai bună — alegerea depinde de scenariu.
Avantajul cheie al JIT este optimizarea adaptivă. JIT poate utiliza informații de profil inaccesibile AOT: tipurile exacte ale obiectelor, frecvența reală a apelurilor, ramificările efective. Acest lucru permite aplicarea de optimizări agresive imposibile în compilarea statică. De exemplu, JIT poate virtualiza apelurile de metode (devirtualize), dacă în practică apare un singur tip de destinatar.
| Criteriu | JIT | AOT |
|---|---|---|
| Timp de instalare | Instantaneu | Depinde de dimensiune |
| Prima lansare | Mai lentă (încălzire) | Rapidă |
| Spațiu pe disc | Minim | +15–30% |
| Adaptabilitate | Ridicată | Scăzută |
| Consum CPU | Vârfuri la compilare | Stabil |
Compilarea JIT este preferată atunci când viteza de implementare și economisirea spațiului pe disc sunt importante. În contextul dezvoltării mobile, JIT este ideal pentru aplicații care se actualizează frecvent (testare A/B, hotfix). JIT este, de asemenea, convenabil în faza de dezvoltare, când codul este recompilat de zeci de ori pe zi — fiecare secundă economisită la compilare accelerează ciclul de feedback.
JIT oferă dezvoltatorilor o serie de avantaje practice. Primul — dimensiunea mică a APK. În abordarea JIT, în APK este ambalat doar bytecodul (DEX), care ocupă cu 20–30% mai puțin spațiu decât codul nativ compilat. Pentru utilizatorii cu memorie internă limitată, acesta este un avantaj semnificativ.
Al doilea avantaj — adaptarea la dispozitiv. JIT compilează codul ținând cont de arhitectura reală a CPU, cantitatea de RAM și încărcarea curentă. De exemplu, pe un dispozitiv cu 2 GB RAM, JIT poate compila mai puțin agresiv, economisind memorie, iar pe un flagship cu 12 GB — să aplice toate optimizările posibile. Compilarea AOT, dimpotrivă, fixează decizia la momentul instalării.
Bytecodul rămâne independent de platformă, ceea ce simplifică distribuirea aplicațiilor. Un singur APK funcționează pe dispozitive ARM, ARM64 și x86, iar JIT asigură generarea de cod nativ pentru fiecare arhitectură. Pentru abordarea AOT, ar fi necesar fie includerea mai multor variante de cod nativ în APK (creșterea dimensiunii), fie compilarea unei versiuni separate pentru fiecare arhitectură.
Principalul dezavantaj al JIT este încălzirea lentă (warm-up delay). Utilizatorul observă încetiniri în primele secunde de funcționare a aplicației, în timp ce JIT compilează metodele hot. În jocuri, aceasta se manifestă ca „bâlbâială” (stuttering) în nivelurile inițiale. În aplicațiile cu animații — tremurări la primele tranziții între ecrane.
Al doilea dezavantaj este consumul de energie. Procesul de compilare încarcă intens CPU, crescând consumul de energie cu 10–20% în perioada de încălzire. Pe dispozitivele alimentate cu baterie, aceasta reduce timpul de funcționare autonomă. Este deosebit de vizibil în scenariile cu reporniri frecvente ale aplicațiilor (multitasking cu memorie limitată, când sistemul descarcă și reîncarcă procesele).
O altă problemă este fragmentarea cache-ului JIT. Codul compilat este stocat într-o zonă continuă de memorie. La încărcarea de noi clase și compilarea de metode suplimentare, cache-ul se fragmentează, ceea ce crește cheltuielile de gestionare a memoriei. În Dalvik, această problemă era rezolvată prin curățarea periodică a cache-ului; în ART, cache-ul JIT este alocat separat de heap și utilizează propria strategie de defragmentare.
Abordarea modernă în ART — compilarea hibridă, care combină punctele forte ale JIT și AOT. La instalarea aplicației, compilarea nu se execută — doar verificarea bytecodului (verify). Aceasta asigură o instalare rapidă și ocuparea unui spațiu minim. Primele lansări funcționează în modul de interpretare cu compilarea JIT a metodelor hot — utilizatorul primește o performanță acceptabilă fără o așteptare lungă.
În paralel, funcționează profilatorul de fundal, care colectează date despre utilizarea reală. După 2–3 lansări complete ale aplicației, profilul atinge o completitudine suficientă, iar sistemul lansează dex2oat pentru a compila metodele hot în cod nativ. Această operație se execută în fundal, atunci când dispozitivul nu este încărcat (încărcare, ecran oprit). După finalizarea AOT în fundal, aplicația obține o performanță comparabilă cu compilarea AOT completă.
# Pornirea forțată a compilării în fundal
adb shell cmd package compile -m speed-profile -f com.example.app
# Vizualizarea stării de compilare
adb shell cmd package dump-profiles com.example.app
Conform datelor Google I/O 2017, compilarea hibridă a redus timpul de instalare a aplicațiilor cu 30–50% față de AOT pur. Spațiul ocupat pe partiția de sistem s-a redus cu 20–30%. Performanța după compilarea în fundal corespunde nivelului AOT complet. Singurul scenariu în care hibridul este inferior AOT este prima lansare imediat după instalare: aplicația rulează în modul JIT și poate fi cu 10–15% mai lentă.
Întrebări frecvente
JIT — este o metodă de accelerare a programului în care codul este tradus în limbaj mașină nu dinainte, ci pe bucăți în timpul funcționării. Cele mai frecvente bucăți sunt compilate și stocate în cache, iar cele rare rămân în forma originală.
JIT compilează codul în timpul execuției, ceea ce economisește spațiu și accelerează instalarea. AOT compilează tot codul dinainte — aplicația pornește mai repede, dar necesită mai mult spațiu pe disc și timp pentru instalare.
JIT nu a fost eliminat, ci a evoluat. În Android 5.0, Dalvik cu JIT a fost înlocuit cu ART cu AOT pur. În Android 7.0, JIT s-a întors în ART ca parte a unui sistem hibrid, unde funcționează împreună cu compilarea AOT în fundal pentru performanță optimă.
JIT crește consumul de energie cu 10–20% în perioada de încălzire din cauza încărcării CPU. După finalizarea compilării metodelor hot, consumul de energie revine la nivelul normal. Modul hibrid ART minimizează aceste vârfuri datorită compilării în fundal.
Da, în scenariile cu calcule intensive. Utilizatorul poate observa încetiniri în primele secunde de funcționare a aplicației sau la începutul jocului. în versiunile moderne de Android (8.0+), modul hibrid minimizează acest efect datorită compilării profilate.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și