JAVA VIRTUAL MACHINE (JVM)
Introduzione
La Java Virtual Machine (JVM) è una macchina astratta stack-based (a parte il program counter,
non ci sono registri Æ tutte le operazioni eseguite dalla macchina virtuale utilizzano lo stack),
multi-threading (è possibile eseguire più thread concorrentemente), type safety (effettua un forte
controllo di tipo a compile time; inoltre la mancanza di puntatori espliciti vieta qualsiasi accesso
“incontrollato” alla memoria).
La JVM è indipendente dalla piattaforma: la JVM e le Java API realizzano una piattaforma (Java
Platform o Java runtime system) su cui vengono eseguite le applicazioni Java, al di sopra (e
indipendentemente) dall’hardware e dal S.O. sottostanti.
La JVM è un interprete del bytecode: la fase di compilazione “traduce” i sorgenti java in un
linguaggio intermedio, il bytecode, memorizzato nei class files il cui contenuto viene eseguito a
runtime dalla macchina virtuale.
La JVM opera su due tipi di dati:
• Primitive types Æ “contengono” i primitive values, che sono scalari
• Reference types Æ “contengono” i reference values, che sono riferimenti ad oggetti (possono
essere pensati come puntatori ad oggetti)
32 bit
64 bit
8 bit
16 bit
32 bit
64 bit
16 bit
-1-
L’architettura della Java Virtual Machine
Il codice contenuto nei class files viene caricato dal class loader nelle strutture a runtime contenute
nella parte tratteggiata; queste interagiscono con l’execution engine della macchina virtuale per
effettuare operazioni che comportino chiamate di sistema (ad es. I/O) o per l’invocazione di metodi
nativi.
•
Method Area Æ È condivisa tra tutti i thread e memorizza le strutture relative alle classi:
- type information (nome qualificato del tipo, nome qualificato del tipo della superclasse,
se il tipo è una classe o un’interfaccia, modificatori di tipo, lista ordinata dei nomi
qualificati delle “superinterfacce”)
- constant pool (è una rappresentazione a runtime della constant pool table del class file e
contiene tutti i riferimenti simbolici ai campi e metodi usati da una classe; i campi sono
referenziati mediante indice come in un array)
- field information (nome dell’attributo, tipo dell’attributo, modificatori dell’attributo)
- method information (nome del metodo, valore di ritorno del metodo, numero e tipi, in
ordine, dei parametri, modificatori del metodo, bytecode del metodo, tavola delle
eccezioni)
- variabili di classe (variabili, dichiarate static, che possono essere accedute anche in
assenza di istanze della classe)
- riferimento alla classe ClassLoader (viene mantenuta traccia di quale class loader,
bootstrap o user-defined, ha caricato una data classe in modo che che tutte le istanze di
quella classe vengano create dallo stesso class loader, creando così un name space
omogeneo)
- riferimento alla classe Class (per ogni tipo/classe caricato, la JVM crea un’istanza della
classe Class tramite la quale si può accedere alle informazioni contenute nella method
area)
Quando la JVM carica un tipo (classe), il class loader legge le informazioni relative dal class
file e le passa nuovamente alla JVM la quale le memorizza nella method area.
-2-
•
Heap Æ viene creato all’avvio della JVM ed è condiviso tra tutti i thread; in esso vi sono
allocate tutte le istanze delle classi e gli array la cui allocazione/deallocazione è affidata al
garbage collector
•
Java Stack Æ ogni thread ha un suo stack privato (creato al momento della creazione del
thread): le operazioni possibili sono push e pop di frame; un frame è composto dalle local
variables (usate per passare i parametri nell’invocazione di metodo), dall’operand stack
(utilizzato per caricare costanti o valori dalle local variables, eseguire operazioni aritmetiche,
…) e dal frame data (contenente informazioni necessarie a runtime per l’esecuzione di un
metodo quali, un puntatore al runtime constant pool, informazioni per il return da un metodo, un
riferimento alla tavola delle eccezioni nella method area)
•
PC registers Æ ogni thread ha il proprio pc (program counter) register: durante l’esecuzione di
un metodo il pc register contiene l’indirizzo dell’istruzione corrente eseguita dal thread; se il
metodo non è native il pc register contiene l’indirizzo dell’istruzione della macchina virtuale
correntemente eseguita; se il metodo è native il valore del pc register è indefinito
•
Native method stacks Æ utilizzati dalla JVM per consentire l’esecuzione di codice nativo
(diverso da Java): quando un thread invoca un metodo nativo, la JVM, invece di fare il push di
un nuovo frame sul Java Stack, “crea” un collegamento direttamente con lo stack del metodo
invocato
Il Class File
Il class file è un file in cui vengono definiti le rappresentazioni di una classe o interfaccia
(informazioni su attributi, metodi e relativo codice) ed è costituito da un flusso di 8 byte; il formato
del class file è il seguente:
ClassFile { u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Le informazioni contenute nel Class File sono organizzate, mediante stringhe chiamate descrittori,
in una struttura chiamata Constant Pool Table che contiene tutte le informazioni simboliche relative
ad una classe, ai suoi attributi e metodi.
A runtime, la JVM ottiene le informazioni relative all’invocazione di un metodo risolvendo i
riferimenti simbolici contenuti nella constant pool: semplificando, tutte le chiamate di metodo si
traducono, nel linguaggio intermedio, in qualcosa del tipo invoca_metodo #n, dove n è un indice
nella constant pool table: a runtime la JVM, per sapere quale metodo invocare, risolverà questo
-3-
riferimento simbolico andando a cercare nella constant pool, contenuta nella method area, l’indice n
(che ovviamente dovrà contenere un descrittore di un metodo).
Le istruzioni della Java Virtual Machine
Le istruzioni della Java Virtual Machine consistono di un opcode che specifica quale operazione
deve essere eseguita, seguita da uno o più operandi che includono su quali valori agisce l'istruzione;
vi sono diverse classi di istruzioni, tra cui, le più importanti, riguardano la creazione/manipolazione
di oggetti, invocazione di metodi, load/store di valori sullo/dallo stack.
Di seguito, viene presentata una brevissima descrizione delle operazioni della macchina virtuale per
la creazione di oggetti e l’invocazione di metodi:
• new Æ crea una nuova istanza di un oggetto e ne fa il push sull’operand stack
• invokespecial Æ invoca un metodo di istanza della classe parent (tramite super)
• invokevirtual Æ invoca un metodo di istanza (quale metodo venga realmente invocato dipende
dal meccanismo di binding dinamico)
• invokeinterface Æ invoca un metodo di interfaccia (quale metodo venga realmente invocato
dipende dal meccanismo di binding dinamico)
• invokestatic Æ invoca un metodo statico (dichiarato static in Java)
-4-