JIT: cos'è la compilazione Just-In-Time e come funziona

Autore: IT Sectr Pubblicato: 2026-04-16 Tempo di lettura: 9 min

JIT (Just-In-Time) è una tecnologia di compilazione dinamica che converte bytecode o rappresentazione intermedia del programma in istruzioni macchina direttamente durante l'esecuzione. In Android, il compilatore JIT è apparso per la prima volta nella versione 2.2 Froyo come parte della macchina virtuale Dalvik e ha accelerato l'esecuzione delle applicazioni di 2–5 volte. Secondo Google, 2024, il JIT moderno in ART combina l'interpretazione con la compilazione profilata dei metodi hot.

Punti chiave

  • JIT — compilazione Just-In-Time: conversione del codice in codice macchina direttamente mentre il programma è in esecuzione.
  • In Dalvik, JIT compilava i metodi hot dopo aver superato la soglia di chiamate (~200 volte).
  • JIT riduce il tempo di installazione e occupa meno spazio della compilazione AOT completa.
  • Lo svantaggio principale è il ritardo di riscaldamento: i primi secondi l'applicazione funziona più lentamente.
  • Nell'ART moderno, JIT è utilizzato in modalità ibrida con ottimizzazione AOT in background.

Cos'è la compilazione JIT?

Just-In-Time (JIT) è un metodo di compilazione in cui il codice sorgente o bytecode viene convertito in istruzioni macchina non in anticipo (come con AOT), ma al momento della prima chiamata alla sezione corrispondente del programma. Il termine “Just-In-Time” significa che la compilazione avviene “giusto in tempo” — immediatamente prima dell'esecuzione.

Il concetto di JIT esiste dagli anni '60, ma ha ottenuto ampia adozione con l'avvento della Macchina Virtuale Java nel 1995. JIT consente di combinare la portabilità del bytecode (scrivi una volta — esegui ovunque) con prestazioni vicine al codice nativo. Nella Java HotSpot VM, il compilatore JIT analizza il codice eseguito e compila solo le sezioni più critiche, risparmiando tempo e memoria.

Come funziona

Il compilatore JIT riceve bytecode come input, lo interpreta e contemporaneamente raccoglie statistiche. Quando una sezione di codice (metodo, ciclo) viene chiamata abbastanza frequentemente, JIT decide di compilarla. Il codice macchina compilato viene memorizzato in una cache — nelle chiamate successive, viene utilizzata la versione già compilata. Questo fornisce un'accelerazione senza dover compilare l'intero programma.

java
// Esempio: un metodo diventa hot dopo chiamate multiple
public class HotMethod {
    private int compute(int n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i * i;
        }
        return sum;
    }
}

// Chiamare 500 volte in un ciclo — JIT compilerà compute
for (int t = 0; t < 500; t++) {
    hot.compute(1000);
}

JIT in Android: Dalvik e ART

In Android, la compilazione JIT ha attraversato tre fasi di evoluzione. La prima fase — Dalvik senza JIT (Android 1.0–2.1): pura interpretazione del bytecode DEX. La seconda fase — Dalvik con JIT (Android 2.2–4.4): l'introduzione del compilatore JIT, che ha accelerato le applicazioni di 2–5 volte. La terza fase — ART con JIT ibrido (Android 7.0+): il ritorno di JIT in una nuova veste.

JIT in Dalvik è stato implementato come un compilatore basato su tracce. Analizzava non singoli metodi, ma catene di istruzioni (tracce) che vengono eseguite frequentemente in sequenza. Questo permetteva di compilare interi percorsi di esecuzione, inclusi più metodi. Questo approccio era efficace per processori mobili con cache di istruzioni piccole, poiché la traccia compilata entrava nella cache L1.

JIT nell'ART moderno

A partire da Android 7.0 Nougat, ART utilizza JIT basato sui metodi — compila singoli metodi basandosi sui profili di esecuzione. Questo JIT funziona significativamente più velocemente di Dalvik JIT: il tempo tipico di compilazione per un metodo è di 0.5–1 ms contro 3–5 ms in Dalvik. Il codice compilato viene memorizzato in un'area di memoria separata (cache di codice JIT) invece che nell'heap dell'applicazione, riducendo la frammentazione.

ParametroDalvik JITART JIT
TipoBasato su tracceBasato sui metodi
Velocità di compilazione3–5 ms/metodo0.5–1 ms/metodo
Soglia di compilazione~200 chiamateDinamica
Cache di codiceNell'heap dell'applicazioneCache di codice JIT
ProfilaturaInternaFile .prof esterni

Rilevamento dei metodi hot e soglie di compilazione

Il meccanismo centrale di JIT è il rilevamento dei metodi hot. Ogni chiamata a un metodo incrementa un contatore interno. Quando il contatore supera la soglia, il metodo viene marcato come “hot” e inviato alla compilazione. In Dalvik, la soglia era fissa (~200 chiamate). In ART, i contatori sono configurati dinamicamente in base alle risorse disponibili del dispositivo.

Il processo di compilazione include diverse fasi. La prima — analisi del bytecode: JIT esamina il flusso di istruzioni e costruisce un grafo del flusso di dati. La seconda — ottimizzazione: inlining di metodi piccoli, eliminazione di codice morto, piegatura delle costanti. La terza — generazione del codice: conversione del grafo ottimizzato in istruzioni macchina per una specifica architettura CPU (ARM, ARM64, x86).

java
// Dimostrazione di inlining — JIT incorporerà il corpo del metodo
public int inlineExample() {
    return square(5);
}

private int square(int x) {
    return x * x;
} // JIT sostituirà la chiamata con return 5 * 5;

OSR — Sostituzione sullo stack (On-Stack Replacement)

Una tecnica speciale di JIT — On-Stack Replacement (OSR). Se un metodo contiene un ciclo lungo che non termina per centinaia di iterazioni, JIT può compilare il ciclo “al volo” e sostituire la versione interpretata con quella compilata direttamente durante l'esecuzione. OSR è particolarmente efficace per attività computazionali: rendering, elaborazione delle immagini, crittografia.

JIT vs AOT: analisi comparativa

JIT e AOT sono due approcci alla compilazione con compromessi opposti. JIT sacrifica la velocità del primo avvio per una dimensione di distribuzione compatta e adattabilità. AOT sacrifica il tempo di installazione e lo spazio su disco per le massime prestazioni dal primo secondo. Nessuno dei due approcci è assolutamente migliore — la scelta dipende dallo scenario.

Il vantaggio chiave di JIT è l'ottimizzazione adattiva. JIT può utilizzare informazioni di profilo non disponibili per AOT: tipi esatti di oggetti, frequenza di chiamata effettiva, pattern di diramazione reali. Questo consente di applicare ottimizzazioni aggressive impossibili con la compilazione statica. Ad esempio, JIT può devirtualizzare le chiamate ai metodi se nella pratica si incontra un solo tipo di ricevitore.

CriterioJITAOT
Tempo di installazioneIstantaneoDipende dalla dimensione
Primo avvioPiù lento (riscaldamento)Veloce
Spazio su discoMinimo+15–30%
AdattabilitàAltaBassa
Utilizzo CPUPicchi durante la compilazioneStabile

Quando scegliere JIT

La compilazione JIT è preferibile quando sono importanti la distribuzione rapida e il risparmio di spazio su disco. Nel contesto dello sviluppo mobile, JIT è ideale per applicazioni che vengono aggiornate frequentemente (test A/B, hotfix). JIT è anche conveniente durante lo sviluppo, quando il codice viene ricostruito decine di volte al giorno — ogni secondo risparmiato sulla compilazione accelera il ciclo di feedback.

Vantaggi della compilazione JIT

JIT offre agli sviluppatori una serie di vantaggi pratici. Il primo — dimensione ridotta dell'APK. Con l'approccio JIT, solo il bytecode (DEX) viene impacchettato nell'APK, che occupa il 20–30% in meno di spazio rispetto al codice nativo compilato. Per gli utenti con memoria interna limitata, questo è un vantaggio significativo.

Il secondo vantaggio è l'adattamento al dispositivo. JIT compila il codice tenendo conto dell'architettura effettiva della CPU, della quantità di RAM e del carico corrente. Ad esempio, su un dispositivo con 2 GB di RAM, JIT può compilare in modo meno aggressivo per risparmiare memoria, mentre su un flagship con 12 GB può applicare tutte le ottimizzazioni possibili. La compilazione AOT, d'altra parte, fissa la decisione al momento dell'installazione.

Indipendenza dalla piattaforma

Il bytecode rimane indipendente dalla piattaforma, semplificando la distribuzione delle applicazioni. Un singolo APK funziona su dispositivi ARM, ARM64 e x86, e JIT genera codice nativo per ogni architettura. L'approccio AOT richiederebbe di includere più varianti di codice nativo nell'APK (aumentando le dimensioni) o compilare una versione separata per ogni architettura.

Svantaggi e limiti di JIT

Lo svantaggio principale di JIT è il ritardo di riscaldamento. L'utente sperimenta rallentamenti nei primi secondi dell'applicazione mentre JIT compila i metodi hot. Nei giochi, questo si manifesta come balbuzie nei livelli iniziali. Nelle applicazioni con animazioni — transizioni brusche tra gli schermi.

Il secondo svantaggio — consumo energetico. Il processo di compilazione carica intensamente la CPU, aumentando il consumo energetico del 10–20% durante il periodo di riscaldamento. Sui dispositivi a batteria, questo riduce la durata della batteria. È particolarmente evidente in scenari con frequenti riavvii delle applicazioni (multitasking con memoria limitata, dove il sistema scarica e ricarica i processi).

Frammentazione della cache

Un altro problema — la frammentazione della cache JIT. Il codice compilato viene memorizzato in un'area di memoria continua. Quando vengono caricate nuove classi e compilati metodi aggiuntivi, la cache si frammenta, aumentando il sovraccarico della gestione della memoria. In Dalvik, questo problema veniva risolto con la pulizia periodica della cache; in ART, la cache JIT è allocata separatamente dall'heap e utilizza una propria strategia di deframmentazione.

Modalità ibrida: il meglio di due mondi

L'approccio moderno in ART — la compilazione ibrida, che combina i punti di forza di JIT e AOT. Durante l'installazione dell'applicazione, non viene eseguita alcuna compilazione — solo la verifica del bytecode (verify). Ciò garantisce un'installazione rapida e un utilizzo minimo dello spazio. I primi avvii funzionano in modalità di interpretazione con compilazione JIT dei metodi hot — l'utente ottiene prestazioni accettabili senza lunghi tempi di attesa.

Parallelamente, un profilatore in background raccoglie dati sull'utilizzo reale. Dopo 2–3 avvii completi dell'applicazione, il profilo raggiunge una sufficiente completezza, e il sistema lancia dex2oat per compilare i metodi hot in codice nativo. Questa operazione viene eseguita in background quando il dispositivo non è sotto carico (caricamento, schermo spento). Una volta completato l'AOT in background, l'applicazione raggiunge prestazioni paragonabili alla compilazione AOT completa.

bash
# Avvio forzato della compilazione in background
adb shell cmd package compile -m speed-profile -f com.example.app

# Visualizza stato compilazione
adb shell cmd package dump-profiles com.example.app

Risultati dell'approccio ibrido

Secondo Google I/O 2017, la compilazione ibrida ha ridotto il tempo di installazione delle applicazioni del 30–50% rispetto all'AOT puro. Lo spazio su disco occupato sulla partizione di sistema è diminuito del 20–30%. Allo stesso tempo, le prestazioni dopo la compilazione in background corrispondono al livello dell'AOT completo. L'unico scenario in cui l'ibrido è inferiore all'AOT è il primo avvio immediatamente dopo l'installazione: l'applicazione funziona in modalità JIT e può essere più lenta del 10–15%.

Domande frequenti

Cos'è la compilazione JIT in parole semplici?

JIT è un modo per accelerare un programma in cui il codice viene tradotto in linguaggio macchina non in anticipo, ma a pezzi durante l'esecuzione. Le sezioni più frequenti vengono compilate e memorizzate nella cache, mentre quelle rare rimangono nella loro forma originale.

In cosa JIT è diverso da AOT?

JIT compila il codice durante l'esecuzione, risparmiando spazio e accelerando l'installazione. AOT compila tutto il codice in anticipo — l'applicazione si avvia più velocemente ma richiede più spazio su disco e tempo di installazione.

Perché JIT è stato rimosso da Android?

JIT non è stato rimosso — si è evoluto. In Android 5.0, Dalvik con JIT è stato sostituito da ART con AOT puro. In Android 7.0, JIT è tornato in ART come parte di un sistema ibrido dove lavora insieme alla compilazione AOT in background per prestazioni ottimali.

Come influisce JIT sul consumo energetico?

JIT aumenta il consumo energetico del 10–20% durante il periodo di riscaldamento a causa del carico della CPU. Una volta completata la compilazione dei metodi hot, il consumo energetico torna a livelli normali. La modalità ibrida di ART minimizza questi picchi attraverso la compilazione in background.

Il riscaldamento di JIT è visibile all'utente?

Sì, in scenari con calcoli intensivi. L'utente può notare rallentamenti nei primi secondi di funzionamento dell'applicazione o all'inizio di un gioco. Nelle versioni moderne di Android (8.0+), la modalità ibrida minimizza questo effetto grazie alla compilazione profilata.

Riepilogo

  • JIT (Just-In-Time) è una compilazione dinamica che converte bytecode in istruzioni macchina durante l'esecuzione.
  • In Android, JIT si è evoluto: basato su tracce in Dalvik → AOT completo → JIT+AOT ibrido nell'ART moderno.
  • I metodi hot vengono rilevati tramite contatori di chiamate e compilati al superamento della soglia (~200 chiamate).
  • OSR (On-Stack Replacement) consente di compilare i cicli lunghi al volo senza interrompere l'esecuzione.
  • Principali vantaggi di JIT: dimensioni ridotte dell'APK, installazione rapida e adattamento al dispositivo.
  • Principali svantaggi: ritardo di riscaldamento, consumo energetico di picco e frammentazione della cache.
  • La modalità ibrida di ART (Android 7.0+) riduce il tempo di installazione del 30–50% mantenendo prestazioni elevate.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche