La macchina virtuale Dalvik è stata un componente chiave del sistema operativo Android, responsabile dell'esecuzione delle applicazioni fino alla versione 4.4 KitKat. Sviluppata da Dan Bornstein, questa VM basata su registri ha sostituito il concetto di JVM standard e ha permesso di ottimizzare l'avvio delle applicazioni su dispositivi mobili con RAM limitata. Secondo Google, 2024, Dalvik garantiva la compatibilità delle applicazioni attraverso la compilazione JIT, convertendo il bytecode DEX in istruzioni macchina direttamente durante l'esecuzione.
Punti chiave
Dalvik è una macchina virtuale con architettura a registri, creata specificamente per la piattaforma Android. Lo sviluppo iniziò nel 2005 dall'azienda di Dan Bornstein, e nel 2007 il progetto fu acquisito da Google. La prima versione commerciale di Dalvik apparve con il rilascio di Android 1.0 nel 2008.
A differenza della Macchina Virtuale Java standard (JVM), Dalvik non esegue bytecode Java. Il compilatore Java converte il codice sorgente in file class, e poi l'utilità dx li traduce nel formato Dalvik Executable (DEX). Questo formato è più compatto dei file class: un'applicazione di 10 MB in formato class occupa circa 6–7 MB in DEX.
Dan Bornstein scrisse Dalvik come progetto per sistemi operativi con risorse limitate. Il nome deriva dal villaggio islandese di Dalvík. Google scelse Dalvik invece di JVM a causa di restrizioni di licenza e della necessità di un'ottimizzazione profonda per processori mobili con architettura ARM. Il sistema guadagnò rapidamente popolarità: entro il 2012, oltre 500 milioni di dispositivi Android eseguivano Dalvik.
Ogni applicazione Android viene eseguita in un processo separato con la propria istanza della VM Dalvik. Ciò garantisce l'isolamento dei dati e la protezione contro il codice dannoso a livello di sistema operativo. Questo approccio combina i vantaggi della virtualizzazione con il sandbox Linux — il malware in un'applicazione non può influenzare i processi vicini.
L'architettura basata su registri di Dalvik differisce fondamentalmente dall'architettura basata su stack della JVM. Invece di operazioni sulla cima dello stack, Dalvik opera con registri — celle virtuali all'interno della VM. Ogni istruzione contiene gli indirizzi dei registri operandi, riducendo il numero di istruzioni per operazione.
La macchina a stack JVM utilizza istruzioni come push, pop e add — per sommare due numeri sono necessarie tre istruzioni. Dalvik risolve lo stesso compito con un'unica istruzione add-int con tre registri. Secondo Android Open Source Project, l'architettura a registri di DEX riduce la dimensione del bytecode in media del 30% rispetto al formato class basato su stack.
Un file DEX (Dalvik Executable) contiene una rappresentazione compressa di tutte le classi dell'applicazione. L'intestazione del file include un checksum, le dimensioni delle sezioni e gli offset. Le sezioni principali sono i pool di stringhe, tipi, prototipi di metodi, campi e il bytecode stesso. Un singolo file DEX può memorizzare fino a 65.536 metodi (il limite è stato rimosso con l'introduzione del multi-dex in Android 5.0).
L'utilità dx, inclusa in Android SDK Build Tools, viene utilizzata per convertire i file class in DEX. Esempio di comando: dx --dex --output=classes.dex myapp.jar. I progetti moderni utilizzano D8, il successore di dx con ottimizzazione migliorata e supporto per le funzionalità Java 8+.
# Conversione da JAR a DEX usando dx
dx --dex --output=classes.dex myapp.jar
# Versione moderna tramite D8
d8 --lib android.jar --output dex/ myapp.jar
Il processo Zygote è un elemento cruciale dell'architettura Dalvik. Quando il sistema si avvia, Zygote carica tutte le classi dell'SDK Android, apre le librerie condivise e crea un pool di risorse precaricate. Quando un utente apre un'applicazione, il sistema duplica il processo Zygote (fork), creando una nuova istanza della VM Dalvik con un framework già inizializzato. Ciò riduce il tempo di avvio dell'applicazione da ~2–3 secondi a 300–500 millisecondi.
JIT (Just-In-Time) è una tecnologia per compilare bytecode in istruzioni macchina direttamente durante l'esecuzione dell'applicazione. In Dalvik, il compilatore JIT analizza il codice DEX in esecuzione, identifica i metodi frequentemente utilizzati (hot) e li compila in codice nativo per la CPU.
La scelta di JIT invece della compilazione completa Ahead-Of-Time (AOT) nelle prime versioni di Android è stata deliberata. I dispositivi mobili avevano memoria flash limitata (4–16 GB) — precompilare tutte le applicazioni avrebbe occupato spazio significativo. Inoltre, la memoria ROM nei primi dispositivi era più lenta della RAM, e leggere codice precompilato avrebbe potuto ridurre le prestazioni.
Quando un'applicazione viene avviata, Dalvik inizia a interpretare il bytecode DEX. Un profiler speciale tiene traccia di quali metodi vengono chiamati più frequentemente. Dopo aver superato una soglia (tipicamente ~200 chiamate), il compilatore JIT converte il metodo in codice macchina e lo memorizza nella cache in RAM. Le chiamate successive utilizzano la versione già compilata senza ricompilazione.
// Esempio di un metodo hot che JIT compilerà
public class Calculator {
public int sumArray(int[] arr) {
int total = 0;
for (int i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
}
Secondo Google I/O 2013, l'introduzione di JIT in Android 2.2 Froyo ha accelerato l'esecuzione delle applicazioni in media di 2–5 volte rispetto all'interpretazione pura. Tuttavia, JIT aggiunge latenza al primo avvio: un'applicazione necessita di 3–10 secondi per riscaldarsi e compilare i metodi hot. Dopo il riscaldamento, le prestazioni si stabilizzano a un livello vicino al codice nativo.
Dalvik differisce dalla JVM in diversi aspetti fondamentali. Primo — architettura: JVM è basata su stack, Dalvik è basata su registri. Secondo — formato del bytecode: JVM utilizza file class, Dalvik utilizza DEX. Terzo — gestione della memoria: Dalvik è ottimizzata per la RAM limitata dei dispositivi mobili.
Entrambi gli approcci hanno i loro punti di forza. La JVM basata su stack richiede meno spazio per memorizzare le istruzioni — ogni istruzione è più corta perché gli operandi vengono implicitamente presi dallo stack. Dalvik basata su registri esegue meno istruzioni per operazione, il che risparmia tempo CPU e riduce il consumo energetico. Per i dispositivi mobili alimentati a batteria, questo è fondamentale.
| Parametro | Dalvik | JVM |
|---|---|---|
| Architettura | Basata su registri | Basata su stack |
| Bytecode | DEX | class |
| Compilazione | JIT (Android 2.2+) | JIT / AOT |
| Ottimizzazione | Basso consumo energetico | Alta compatibilità |
| Isolamento | Tramite processi Linux | Tramite ClassLoader |
La scelta di Dalvik anziché JVM è stata anche motivata dalle licenze. Oracle possiede i diritti su Java SE e JVM, e Google cercava di evitare le royalty. Creare una propria VM con un formato di bytecode alternativo ha permesso ad Android di svilupparsi indipendentemente da Oracle. Questa disputa si è trasformata in una lunga battaglia legale, Oracle contro Google (2010–2021), terminata a favore di Google.
DEX (Dalvik Executable) è un formato binario contenente il codice compilato di un'applicazione Android. Ogni file DEX inizia con un'intestazione, seguita da sezioni: costanti di stringa (string_ids), tipi (type_ids), prototipi di metodi (proto_ids), campi (field_ids), metodi (method_ids), definizioni di classi (class_defs) e un'area dati.
L'utilità dx converte i file class Java in uno o più file DEX. L'algoritmo include la deduplicazione delle costanti — stringhe o tipi identici vengono memorizzati una volta e referenziati per indice. Ciò riduce significativamente la dimensione finale. Nei progetti moderni, dx è stato sostituito da D8 (introdotto in Android Studio 3.1), che è 2–3 volte più veloce e supporta il desugaring di Java 8.
// Esempio di bytecode DEX decompilato tramite dexdump
// Codice sorgente: return a + b;
@Ldalvik/annotation/Code;
registers: 3
add-int v0, v1, v2
return v0
La limitazione del formato DEX a 65.536 metodi (limite dell'indice a 16 bit) è diventata un serio problema per le applicazioni di grandi dimensioni. La soluzione è arrivata con Android 5.0: il supporto multi-dex consente a un'applicazione di contenere più file DEX. Il file principale classes.dex contiene i punti di ingresso, mentre i file aggiuntivi classes2.dex, classes3.dex e così via contengono il resto del codice. La configurazione multi-dex viene abilitata in build.gradle con la riga multiDexEnabled true.
La garbage collection in Dalvik è implementata come un collector generazionale con mark-and-sweep. La memoria è divisa in due aree principali: Heap (mucchio) per gli oggetti e Stack (pila) per i primitivi e i riferimenti. Quando l'Heap si riempie, Dalvik sospende tutti i thread (STW — Stop-The-World), marca gli oggetti raggiungibili e libera quelli irraggiungibili.
Prima di Android 2.2, Dalvik utilizzava un collector a thread singolo con durate di pausa fino a 100–200 ms. Android 2.3 Gingerbread ha introdotto un collector concorrente che ha ridotto le pause tipiche a 5–10 ms. E Android 4.0 Ice Cream Sandwich ha aggiunto un collector con pulizia incrementale — Concurrent Mark and Sweep (CMS).
Un problema tipico delle applicazioni Dalvik sono le perdite di memoria attraverso riferimenti statici ad Activity. Se un campo statico mantiene un riferimento a Context o View, il garbage collector non può liberare l'Activity nemmeno dopo la chiusura dello schermo. Strumenti come Eclipse MAT e LeakCanary aiutano a rilevare tali perdite: analizzano un dump dell'Heap e mostrano le catene di riferimenti che trattengono l'oggetto.
// Esempio di perdita di memoria tramite riferimento statico
public class Utils {
private static Context context;
public static void init(Context ctx) {
context = ctx; // Mantiene Activity dopo finish()
}
}
Nonostante il suo successo, Dalvik presentava diversi inconvenienti. La compilazione JIT richiedeva tempo di riscaldamento — i primi secondi di funzionamento dell'applicazione erano più lenti. Inoltre, JIT consumava energia della CPU durante la compilazione, riducendo la durata della batteria. Con l'aumento delle prestazioni dei dispositivi mobili e l'incremento della memoria integrata, la necessità di JIT è diminuita.
In Android 4.4 KitKat, Google ha presentato ART (Android Runtime) come sostituto sperimentale di Dalvik. A partire da Android 5.0 Lollipop, ART è diventato l'unico ambiente di esecuzione. La differenza principale è la compilazione AOT: invece di compilare durante l'esecuzione, tutte le applicazioni vengono compilate in codice macchina durante l'installazione. Ciò ha eliminato i ritardi di riscaldamento e migliorato l'efficienza energetica.
La transizione da Dalvik ad ART è stata trasparente per gli sviluppatori: entrambi gli ambienti di esecuzione eseguono lo stesso bytecode DEX. Le applicazioni compilate per Dalvik funzionano su ART senza ricompilazione — system_server le compila in codice nativo durante l'installazione. L'eccezione è il codice che utilizza la riflessione per accedere ai membri interni della VM Dalvik: tale codice potrebbe rompersi su ART a causa di cambiamenti nell'architettura interna.
Domande frequenti
Dalvik è un programma intermediario che esegue le applicazioni Android sul telefono. Prende il codice dell'applicazione e lo converte in comandi comprensibili dal processore, facendolo direttamente mentre l'utente lavora.
Dalvik utilizza un'architettura basata su registri e il formato DEX, mentre la JVM utilizza un'architettura basata su stack e il formato class. Dalvik è ottimizzata per dispositivi mobili con memoria e potenza di elaborazione limitate, mentre la JVM è progettata per computer desktop e server.
ART offre prestazioni superiori grazie alla compilazione AOT anticipata — l'applicazione viene compilata una volta durante l'installazione, non ogni volta che viene avviata. Ciò accelera il funzionamento e risparmia batteria rispetto all'approccio JIT di Dalvik.
Sì, ART è completamente retrocompatibile con il bytecode DEX di Dalvik. Durante l'installazione, ART compila i vecchi file DEX in codice nativo. L'eccezione sono le applicazioni che utilizzano la riflessione per accedere ai meccanismi interni di Dalvik.
DEX (Dalvik Executable) è un formato di file eseguibile contenente bytecode compresso di un'applicazione Android. Un singolo APK può contenere più file DEX (multi-dex) se l'applicazione ha più di 65.536 metodi.
Riepilogo
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.
Leggi anche