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-