Lasciatemi spiegare con l'esempio di un'applicazione Java desktop.
L'applicazione è solitamente compilata dal sorgente Java in file di classe (bytecode) e impacchettata in file Zip con un'estensione speciale ".jar" e conservata nell'hard disk (es. hard disk Seagate 500GB).
Il JAR viene eseguito con l'aiuto della Java Virtual Machine (dovrebbe essere già installato) nel PC/Mac.
Una istanza della JVM viene avviata per ogni applicazione Java. (La JVM stessa è un'applicazione nativa sul PC/Mac - quindi, essa stessa è un programma che viene caricato ed eseguito - non lo tratterò qui).
La JVM carica le classi dell'applicazione una per una quando sono necessarie - e le tiene nella RAM. In questa fase, si tratta ancora di bytecode.
La JVM legge brevi frammenti delle prossime centinaia di istruzioni bytecode e li traduce just-in-time (JIT) in comandi in linguaggio assembly (comandi specifici della CPU - esempio set di istruzioni Intel Core i3/AMD Ryzen/Qualcomm Snapdragon) e li carica nella cache della CPU - cache L1/L2 sulla CPU stessa (di solito circa 128KB a 2MB). Da lì, la CPU eseguirà le istruzioni una per una. Una volta che una sequenza è completata, il JIT metterà il prossimo set di istruzioni per la CPU.
Il JIT prenderà anche in considerazione il sistema operativo mentre fa la conversione in istruzioni della CPU.
Per esempio, se il programma Java sta cercando di aprire un file - il bytecode rimane lo stesso, ma il JIT produrrà un diverso set di istruzioni per Windows e Linux per lo stesso processore Core i3.
Il sistema JIT mette in cache anche alcuni frammenti convertiti da bytecode ad assemblaggio del codice eseguito di frequente, in modo che quelle sezioni vengano eseguite ad alta velocità.
Le classi non vengono quasi mai scaricate dalla RAM.
I dati del programma (risorse, input dell'utente, risultati dei calcoli, etc.) possono essere caricati nella RAM e scaricati mentre il programma viene eseguito. I calcoli sono effettivamente eseguiti spostando i dati dalla RAM ai registri della CPU, facendo i calcoli e poi spostando i risultati di nuovo dai registri della CPU alla RAM.
Il tutto diventa un po' più complicato se si tratta di un programma Java per un sistema embedded o un'applicazione mobile Java.