Come il compilatore JIT ottimizza il codice
Quando viene scelto un metodo per la compilazione, la JVM fornisce i propri bytecode al compilatore Just - In - Time (JIT). JIT deve comprendere la semantica e la sintassi dei bytecode prima di poter compilare correttamente il metodo.
Per consentire al compilatore JIT di analizzare il metodo, i bytecode vengono riformulati per la prima volta in una rappresentazione interna denominata trees, che assomiglia più al codice macchina che ai bytecode. Analisi e ottimizzazioni vengono quindi eseguite sulle strutture ad albero del metodo. Alla fine, le strutture ad albero sono tradotte in codice nativo. Il resto di questa sezione fornisce una breve panoramica delle fasi della compilazione JIT. Per ulteriori informazioni, consultare Diagnosi di un problema JIT o AOT.
Il compilatore JIT può utilizzare più di un thread di compilazione per eseguire le attività di compilazione JIT. L'utilizzo di più thread può potenzialmente aiutare le applicazioni Java ad avviarsi più velocemente. In pratica, più thread di compilazione JIT mostrano miglioramenti delle prestazioni solo quando ci sono core di elaborazione non utilizzati nel sistema.
La compilazione si compone delle seguenti fasi. Tutte le fasi tranne la generazione di codice nativo sono codice multipiattaforma.
Fase 1 - allineamento
- Allineamento semplice
- Allineamento grafico chiamate
- Eliminazione della ricorsione della coda
- Ottimizzazioni call guard virtuali
Fase 2 - ottimizzazioni locali
- Ottimizzazioni e analisi dei flussi di dati locali
- Registra ottimizzazione utilizzo
- Semplificazioni dei modi di dire Java
Fase 3 - ottimizzazione del flusso di controllo
- Riordino, suddivisione e rimozione del codice
- Riduzione e inversione del loop
- Striding del loop e movimento del codice invariante del loop
- Srotolamento e sbucciatura del loop
- Controllo delle versioni e specializzazione dei loop
- Ottimizzazione gestita da eccezioni
- Analisi switch
Fase 4 - ottimizzazioni globali
- Ottimizzazioni e analisi dei flussi di dati globali
- Eliminazione della ridondanza parziale
- Analisi di escape
- Ottimizzazioni GC e allocazione memoria
- Ottimizzazioni di sincronizzazione
Fase 5 - generazione codice nativo
I processi di generazione del codice nativo variano, a seconda dell'architettura della piattaforma. Generalmente, durante questa fase della compilazione, gli alberi di un metodo vengono tradotti in istruzioni di codice macchina; alcune piccole ottimizzazioni vengono eseguite in base alle caratteristiche dell'architettura. Il codice compilato viene inserito in una parte dello spazio del processo JVM denominato code cache; l'ubicazione del metodo nella code cache viene registrata, in modo che le chiamate future ad esso richiamino il codice compilato. In qualsiasi momento, il processo JVM è costituito dai file eseguibili JVM e da una serie di codici compilati JIT collegati dinamicamente all'interprete bytecode nella JVM.