Era il mese di ottobre 2008 quando negli Stati Uniti venne presentato HTC Dream, il primo dispositivo mobile basato sul sistema operativo Android: aveva 192 MB di memoria RAM a bordo.
A gennaio 2007 era nato il primo iPhone della storia di Apple: proprio nelle scorse settimane la società oggi guidata da Tim Cook ha festeggiato la ricorrenza. L’iPhone degli albori era dotato di 128 MB di RAM e la stessa scelta fu conservata nel caso del modello successivo.
Nei periodi seguenti sia i dispositivi Android che quelli lanciati sul mercato da Apple fecero registrare un raddoppio annuale per quanto riguarda la RAM disponibile fino al 2012 quando ad esempio sia il Samsung Galaxy S3 che l’iPhone 5 avevano 1 GB di RAM.
Alcune versioni del Galaxy S3 disponevano di 2 GB e i modelli di generazione successiva, i Galaxy S4, arrivarono sul mercato con 2 GB su tutte le versioni mentre l’iPhone rimase ancorato a 1 GB di RAM con il sistema operativo iOS. La Mela continuò a puntare su questa configurazione per altri due anni fino alla presentazione dell’iPhone 6s.
Da qui in avanti su Android la RAM in dotazione è cresciuta molto contrariamente a quanto avveniva su iOS con aumenti decisamente più contenuti.
Oggi la maggior parte degli iPhone usa 4 GB di memoria RAM mentre ad esempio l’iPhone 13 Pro si spinge a 6 GB. Lato Android, invece, si trovano un’infinità di dispositivi con 6 o 8 GB di RAM con i top di gamma che si spingono fino a 12 o 16 GB di RAM.
La dotazione di memoria RAM deve essere sufficiente per sostenere le app mantenute contemporaneamente in esecuzione dagli utenti. Ma perché queste differenze?
Concentriamoci sui linguaggi di programmazione.
I programmatori che sviluppano app per iOS usano linguaggi come Objective-C e Swift. Essi adottano un approccio tradizionale: si sviluppa l’applicazione, si compila il suo codice e si ottiene un’app nativa in formato binario che altrettanto nativamente può essere eseguita dal processore.
Nel caso di un’app nativa le istruzioni vengono immediatamente gestite dal processore che sa comprenderle e mandarle subito in esecuzione.
Parlando dei dispositivi Apple c’è un’unica configurazione hardware con la quale gli sviluppatori hanno a che fare e che è basata su SoC Apple con architettura di derivazione ARM.
Su Android le cose sono un po’ differenti: agli albori del sistema operativo fu deciso di basarsi su Java come linguaggio di programmazione (oggi si utilizza ampiamente Kotlin).
Java è diverso ad esempio da Objective-C e Swift perché non produce codice nativo compilato ma usa un linguaggio intermedio chiamato bytecode capace di descrivere le operazioni che costituiscono un programma.
Il bytecode riduce drasticamente la dipendenza dall’hardware e facilita la creazione degli interpreti del linguaggio stesso. Ecco perché già nel 2016 secondo Oracle oltre 15 miliardi di dispositivi usavano Java e nel 2017 c’erano attive ben 38 miliardi di Java Virtual Machine (JVM, vedere più avanti): Java può funzionare su PC, server, dispositivi mobili, device per l’Internet delle Cose, macchine virtuali, sui single-board computer Raspberry, sulle smart TV e in molti altri contesti. Lo slogan di Java è infatti da sempre stato “write once, run everywhere“.
Usando il bytecode non c’è insomma alcun bisogno di compilare il codice una volta per un SoC ARM, un’altra volta per un processore Intel o AMD basato su architettura x86, un’altra per qualche altra tipologia di chip. Un vantaggio enorme.
Lo svantaggio consiste nella necessità di usare un runtime environment che si occupa di far funzionare il bytecode sullo specifico processore: la Java Virtual Machine, cui abbiamo fatto riferimento in precedenza. La JVM esegue i programmi in bytecode, nello stesso bytecode, sulle varie piattaforme.
Rispetto alla compilazione nativa la JVM aggiunge un livello aggiuntivo pur usando diverse tecniche che consentono di convertire il bytecode o comunque parti di esso in codice nativo.
Lasciando da parte le prestazioni (non ci occupiamo di questo aspetto), il fatto che vi sia la necessità di ricorrere a un runtime environment implica l’utilizzo di un maggiore quantitativo di memoria rispetto ad esempio all’approccio tradizionale abbracciato da Apple.
Verificando l’occupazione in termini di memoria RAM delle singole applicazioni appena installate su Android e iOS si può verificare facilmente che quelle per il robottino verde sono in molti casi decisamente più esigenti (leggasi numero di megabyte impegnati in RAM).
Ovviamente dando per scontato che la prova sia effettuata nelle medesime condizioni (medesimi account, stessi documenti aperti e così via), ci sono casi in cui alcune app risultano fino al 70% più esigenti in termini di RAM su Android piuttosto che su iOS. In generale si registra comunque un +40% sul versante dell’occupazione RAM per le app Android se confrontate con le corrispondenti versioni per iOS.
Le cose cambiano ad esempio con tanti videogiochi che usano piattaforme come Unreal oppure Unity: essi si servono di soluzioni espressamente concepite per realizzare codice che funziona nativamente su iOS e su Android. In questo modo, ad esempio, è possibile sfruttare al massimo il potenziale dell’hardware sottostante con particolare riferimento alle caratteristiche della GPU.
Effettuando delle prove comparative nel caso dei videogiochi si scopre che l’occupazione in RAM è più marcata in iOS rispetto ad Android per alcuni titoli, viceversa per altri. Le differenze sono spesso nell’ordine del 10% e tutto questo perché si ha a che fare con codice nativo. Le difformità sul piano dell’occupazione della RAM sono qui riconducibili ad altri fattori, comunque importanti, come i compilatori utilizzati, la gestione delle texture, la loro compressione, la risoluzione dello schermo, le librerie grafiche impiegate e altro ancora.
La memoria RAM è una risorsa finita nel senso che sia su Android che su iOS ne è disponibile un quantitativo ben preciso a seconda della dotazione hardware del dispositivo in uso. Nel caso in cui la memoria disponibile non fosse sufficiente per gestire le applicazioni via via caricate, entrambi i sistemi operativi mettono in atto un’operazione che può essere descritta come una compressione dei dati presenti in RAM.
Quando alcune pagine della memoria non vengono utilizzate da un po’ e ci sono scarse probabilità che possano esserlo nel breve termine, allora il sistema operativo comprime i dati in esse contenuti in modo da liberare spazio prezioso. Gli algoritmi usati in Android e iOS sono ovviamente differenti ma l’idea di fondo è la stessa.
In Android si usa zRAM, un modulo ereditato dal kernel Linux che si occupa di comprimere appunto le pagine presenti in memoria RAM e di decomprimere i dati quando necessario ovvero quando i dati devono essere elaborati.
In alcuni casi zRAM utilizza una porzione della memoria RAM espressamente dedicata alla compressione dei dati mentre alcuni produttori hanno deciso di implementare un meccanismo di swapping che somiglia al concetto di memoria virtuale adottato in Windows, Linux e macOS: alcuni dati in origine posti in RAM vengono in questo caso temporaneamente salvati nella memoria flash o storage interno del dispositivo mobile.
In situazioni di emergenza, quando non c’è modo di comprimere efficacemente i dati in RAM, allora entrambi i sistemi operativi rimuovono le app precedentemente caricate in memoria usando approcci diversi dei quali abbiamo parlato nell’articolo su quanta RAM deve avere uno smartphone.
In generale, però, le applicazioni che rimangono inutilizzate da più tempo e che sono ancora caricate in memoria (risultano in esecuzione) vengono chiuse (killed).
A spanne, quindi, se per un iPhone vanno bene 4 GB di RAM per un dispositivo Android bisognerebbe considerare dispositivi con almeno il 40% di dotazione di memoria RAM in più quindi da 6 GB a salire. Questo per utilizzi “tipici” di uno smartphone e per le necessità che affronta ad esempio ogni giorno un professionista. Ovvio che i videogiochi alzano l’asticella su entrambi i sistemi operativi.