Runtime è uno strato software che gestisce l'esecuzione del codice di un'applicazione mobile: alloca memoria, gestisce eccezioni, esegue la garbage collection e smista le chiamate ai metodi. Senza runtime, nessuna applicazione può essere eseguita — è lo strato intermedio tra il codice compilato e il sistema operativo. Secondo Android Developer Documentation, 2025, l'ambiente di esecuzione è un elemento chiave della piattaforma che determina prestazioni e compatibilità.
Punti chiave
Runtime è l'infrastruttura che garantisce l'esecuzione del programma dopo il suo avvio. Nel contesto dello sviluppo mobile, il runtime include il caricatore di classi, l'allocatore di memoria, il garbage collector, lo smistatore di metodi e il gestore di eccezioni. Senza questo strato intermedio, il sistema operativo non può eseguire il bytecode Dalvik o i messaggi Objective-C.
Le piattaforme mobili utilizzano diverse implementazioni di runtime. Android usa ART (Android Runtime) con compilazione ibrida AOT/JIT. iOS usa Objective-C Runtime — un sistema dinamico basato su message passing e identificatori SEL. Entrambi gli approcci risolvono lo stesso problema: eseguire il codice dello sviluppatore su un dispositivo specifico con le massime prestazioni.
Secondo Google I/O 2024, Android Runtime elabora oltre 10 miliardi di metodi al giorno su dispositivi in tutto il mondo. Le prestazioni del runtime influenzano direttamente la velocità di avvio dell'applicazione, la fluidità delle animazioni e il consumo della batteria. Ogni chiamata a metodo, ogni allocazione di memoria e ogni ciclo di garbage collection passano attraverso lo strato runtime.
Runtime system include cinque componenti chiave: caricatore di classi, gestore di memoria, interprete o compilatore, smistatore di metodi e sistema di sicurezza. Ogni componente svolge una funzione strettamente definita nel processo di esecuzione del codice.
Quando un utente avvia un'applicazione, il ClassLoader carica i file DEX (Android) o i binari Mach-O (iOS) nella RAM. In Android, questa fase include la verifica del bytecode: il runtime controlla che il codice non contenga istruzioni non sicure, non superi i limiti degli array e rispetti i tipi. La verifica è un passaggio di sicurezza critico che impedisce l'esecuzione di codice dannoso.
Gestore di memoria alloca e libera memoria per gli oggetti. In Android ART viene utilizzato un garbage collector concorrente con raccolta generazionale: gli oggetti giovani vengono controllati più spesso, quelli vecchi meno spesso. Objective-C Runtime utilizza Automatic Reference Counting (ARC), dove il compilatore inserisce automaticamente le chiamate retain/release.
Smistatore di metodi determina quale implementazione del metodo verrà chiamata. Nei linguaggi statici (Kotlin, Swift), lo smistamento viene effettuato tramite vtable — una tabella di metodi virtuali. Nei linguaggi dinamici (Objective-C), il messaggio passa attraverso objc_msgSend, che cerca l'implementazione nella classe e nelle sue superclassi. Il risultato viene memorizzato nella cache nel method cache per accelerare le chiamate ripetute.
Android Runtime (ART) è una macchina virtuale che esegue il bytecode DEX delle applicazioni Android. ART ha sostituito Dalvik in Android 5.0 Lollipop, introducendo la compilazione AOT: l'applicazione viene compilata in codice macchina una volta durante l'installazione. Ciò ha eliminato il sovraccarico della compilazione JIT a ogni avvio.
A partire da Android 7.0 Nougat, ART utilizza un approccio ibrido. Durante l'installazione, la compilazione JIT viene eseguita solo per i metodi usati frequentemente (hot methods), il resto del codice viene interpretato. Un processo in background (profile-guided optimization) analizza quali metodi vengono chiamati più spesso e li compila AOT durante il tempo di inattività del dispositivo. Ciò riduce il tempo di installazione garantendo prestazioni elevate.
ART include anche un compilatore AOT (dex2oat) che converte i file DEX in binari ELF con codice macchina ARM64. La compilazione viene eseguita con tre livelli di ottimizzazione: quicken (veloce), optimize (medio) e everything (completo). Per impostazione predefinita, Android utilizza optimize, bilanciando tra velocità di compilazione e prestazioni del codice.
class RuntimeExample {
fun measureExecutionTime() {
val start = System.nanoTime()
// Chiamata a metodo compilato da ART
processData()
val end = System.nanoTime()
println("Tempo di esecuzione: ${end - start} ns")
}
}
Nell'esempio sopra, System.nanoTime() è un metodo nativo la cui chiamata viene smistata attraverso il runtime ART nel kernel Linux. ART converte il bytecode Kotlin in istruzioni ARM64 che vengono eseguite dal processore del dispositivo. Questo processo avviene in modo trasparente per lo sviluppatore, ma la sua ottimizzazione è un compito chiave del team Android Platform.
Profile-guided optimization è un meccanismo di ART che raccoglie profili di utilizzo dei metodi. Il file profiles/
Lo sviluppatore può abilitare i baseline profiles nel proprio progetto Gradle. Si tratta di annotazioni manuali che indicano ad ART quali metodi compilare AOT immediatamente dopo l'installazione. I baseline profiles riducono il primo avvio del 40% senza attendere la profilazione in background.
Objective-C Runtime è una libreria dinamica che fornisce l'esecuzione del codice Objective-C su iOS e macOS. Il suo nucleo è la funzione objc_msgSend, che implementa il message passing: invece di una chiamata diretta a metodo, l'oggetto invia un messaggio con un selettore, e il runtime determina quale implementazione deve essere eseguita.
Ogni oggetto Objective-C contiene un puntatore isa alla sua classe, e la classe ha una dispatch table che mappa i selettori (SEL) alle implementazioni (IMP). Quando viene chiamato un metodo, objc_msgSend percorre la catena: classe → superclasse → NSObject, fino a trovare l'IMP. Se non viene trovata alcuna implementazione, il runtime invoca il forwarding mechanism, che può intercettare il messaggio o generare un'eccezione.
Objective-C Runtime supporta anche il method swizzling — la sostituzione dell'IMP di un selettore esistente in fase di esecuzione. Questo è un meccanismo potente utilizzato nelle librerie AOP e negli strumenti di monitoraggio, ma richiede cautela a causa del suo impatto sull'intera applicazione.
@interface RuntimeDemo : NSObject
- (void)printClassInfo;
@end
@implementation RuntimeDemo
- (void)printClassInfo {
// objc_getClass — funzione runtime
Class cls = objc_getClass("RuntimeDemo");
unsigned int count;
Method *methods = class_copyMethodList(cls, &count);
NSLog("Numero di metodi: %d", count);
}
@end
Il codice dimostra l'accesso diretto all'API di Objective-C Runtime: objc_getClass ottiene l'oggetto classe per nome, class_copyMethodList recupera l'elenco di tutti i metodi. Questa è la riflessione in azione — accesso ai metadati della classe in fase di esecuzione. Questo approccio viene utilizzato in XCTest per la registrazione dinamica dei test.
Puntatore isa è un puntatore alla classe dell'oggetto, memorizzato nei primi 8 byte di ogni oggetto. A partire da iOS 12, Apple ha introdotto isa-swizzling per l'ottimizzazione: i bit inferiori di isa codificano informazioni aggiuntive sullo stato dell'oggetto. I tagged pointers sono un'altra ottimizzazione in cui valori fino a 60 bit (NSNumber, NSDate) vengono memorizzati direttamente nel puntatore, senza allocare un oggetto nell'heap. Ciò riduce il carico del gestore di memoria del 30%.
JIT (Just-In-Time) e AOT (Ahead-Of-Time) sono due approcci alla compilazione del bytecode in codice macchina. JIT compila il codice durante l'esecuzione dell'applicazione, analizzando i punti caldi e ottimizzandoli al volo. AOT compila tutto il codice in anticipo — durante l'installazione dell'applicazione o lato sviluppatore.
| Caratteristica | JIT | AOT |
|---|---|---|
| Tempo di compilazione | Durante l'esecuzione | Durante installazione/build |
| Dimensione APK/IPA | Minore (solo bytecode) | Maggiore (codice macchina) |
| Velocità di avvio | Inferiore (compilazione necessaria) | Superiore (codice pronto) |
| Ottimizzazione per dispositivo | Sì (adattativa) | Limitata (generica) |
| Consumo RAM | Maggiore (compilatore in memoria) | Inferiore |
L'approccio ibrido di ART (Android 7+) è considerato ottimale: l'applicazione utilizza un interprete per i metodi raramente chiamati, JIT per gli hot methods e AOT per i metodi da profile-guided optimization. iOS, al contrario, utilizza AOT stretto tramite LLVM: Swift e Objective-C vengono compilati in codice macchina nella fase di build in Xcode.
Secondo Apple Developer Documentation, 2024, il runtime Swift aggiunge circa 15 MB alla dimensione dell'applicazione. Flutter utilizza la propria Dart VM, dove la compilazione JIT funziona in modalità debug per hot reload e AOT in modalità release per le massime prestazioni. React Native utilizza Hermes — un motore JavaScript con compilazione AOT che riduce il tempo di avvio del 50%.
ARM64 Runtime è il livello in cui il codice macchina interagisce con il processore del dispositivo. La maggior parte dei dispositivi mobili moderni funziona su processori ARM64 (aarch64). Il runtime traduce il bytecode o le chiamate native in istruzioni ARM64 che la CPU esegue.
Registri ARM64 chiave utilizzati dal runtime: x0–x7 (parametri di funzioni), x8 (risultato indiretto), x30 (indirizzo di ritorno), sp (stack pointer), fp (frame pointer). ART genera codice che segue l'ARM64 Procedure Call Standard: tutte le chiamate ai metodi passano attraverso il protocollo definito dall'architettura del processore.
Comprendere l'ABI ARM64 è importante per l'ottimizzazione delle prestazioni: l'inline caching, la predizione dei branch e l'allineamento del codice in memoria influenzano direttamente la velocità del runtime. Gli strumenti di profilazione (Android Studio Profiler, Instruments) mostrano quali sezioni di codice trascorrono più tempo nel runtime — ottimizzarle porta il miglioramento maggiore.
// Esempio di assembly ARM64 generato da ART
// Chiamata a metodo con due parametri
mov x0, x23 // self (this)
mov x1, x24 // param1
mov x2, x25 // param2
bl methodEntryPoint // chiamata tramite runtime
str x0, [sp, #8] // salvare il risultato
In questo esempio, le istruzioni ARM64 mov passano gli argomenti ai registri x0–x2, bl chiama il punto di ingresso del metodo, e str salva il valore di ritorno. Il runtime genera tali istruzioni per ogni chiamata a metodo, ottimizzando la sequenza attraverso devirtualization e inlining.
Runtime overhead è il costo inevitabile dell'invio dinamico. Ogni chiamata a metodo attraverso il runtime richiede: ricerca dell'implementazione nella dispatch table, controllo dei tipi, chiamata all'IMP e restituzione del risultato. Le misurazioni mostrano che il runtime aggiunge 10–50 ns per chiamata in Objective-C e 5–20 ns in ART.
Per ridurre il sovraccarico, gli sviluppatori utilizzano monomorphic inlining (ART) e method caching (Objective-C). Kotlin/Native e Swift compilano direttamente in ARM64, eliminando completamente lo strato runtime, ma perdendo le capacità dinamiche — riflessione, swizzling, caricamento dinamico delle classi.
Domande frequenti
SDK (Software Development Kit) è un insieme di strumenti per lo sviluppo di applicazioni (compilatore, librerie, utilità). Runtime è l'ambiente in cui l'applicazione già sviluppata viene eseguita sul dispositivo. Lo sviluppatore ha bisogno dell'SDK, l'utente ha bisogno del runtime.
No — il runtime fa parte del sistema operativo e non può essere sostituito dall'utente. ART è incorporato in Android Framework, Objective-C Runtime in iOS. Lo sviluppatore può scegliere il linguaggio (Kotlin/Native senza runtime) o utilizzare macchine virtuali come Dart VM in Flutter.
Sì, il runtime influisce sul consumo energetico. La garbage collection in ART e Swift runtime utilizza la CPU, aumentando il consumo della batteria. Ottimizzazioni come concurrent GC e tagged pointers in iOS riducono l'impatto del runtime sulla batteria del 20–30%.
Runtime error è un errore che si verifica durante l'esecuzione: null pointer exception, index out of bounds, divisione per zero. A differenza degli errori in fase di compilazione, non vengono rilevati durante il build. Vengono intercettati tramite blocchi try-catch o crash reporting (Firebase Crashlytics, Sentry).
Swift runtime è più leggero di Objective-C: non supporta l'invio dinamico per impostazione predefinita, utilizza value types (struct) senza allocazione nell'heap e non ha message forwarding. I metodi Swift vengono chiamati direttamente tramite vtable a meno che non siano contrassegnati come @objc dynamic. Ciò offre un miglioramento della velocità fino a 5x nei benchmark.
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