Mașina Virtuală Java Budai László Java este un termen la modă în lumea tehnologiei informatice. Cu toții știm că acest limbaj de programare a dat aripi noi lumii informatice din ultimii ani, în primul rând prin faptul că un program Java, numit aplet, poate să ruleze într-o pagină Web, mărind astfel posibilitățile de comunicare a acestora. Dar limbajul Java nu se rezumă doar la aplicații tip aplet, ci dă cale liberă programatorilor pentru realizarea aplicațiilor de sine stătătoare. O aplicație de sine stătătoare, scrisă în Java, este compilată și rulează pe o așa numită Mașină Virtuală Java. Acest lucru a făcut posibilă separarea de platformă a limbajului. Astfel, un program Java nu este compilat pentru o platformă anume (Sun, MacOS, Win32) ci este transformat în codul Mașinii Virtuale Java. Mașina Virtuală Java reprezintă un calculator abstract. Ca și calculatoarele reale, aceasta dispune de un set de instrucțiuni, un set de registre și utilizează diferite zone de memorie. Acestea sunt componente logice abstracte. Ele nu impun o implementare anume, acest lucru înseamnă că Mașina Virtuală Java se poate implementa ca interpretor de cod binar, compilator (generează cod nativ), sau pe Siliciu, dar să se păstreze funcționalitatea. Setul de instrucțiuniSetul de instrucțiuni Java este echivalentul de limbaj de asamblare a unei aplicații Java. Ca și o aplicație C, o aplicație Java este compilată dar rezultatul este un cod binar Java și nu un cod binar al unui microprocesor anume. O instrucțiune Java este formată dintr-un cod operație (opcode) și operanzi. Codul operație are lungimea de un octet, operandul putând avea lungime variabilă. Dacă un operand are lungimea mai mare decât un octet, atunci se va păstra în memorie, după modelul big-endian", adică octetul mai semnificativ primul. Astfel, o valoare stocată pe 16 biți se va calcula după cum urmează: primul_octet*256+al_doilea_octet. Instrucțiunile binare Java prelucrează operanzii din memoria de date a Mașinii Virtuale Java, ca aparținând unui grup redus de tipuri primitive. Aceste tipuri primitive de date sunt: întreg lung, numere în virgulă flotantă cu simplă și dublă precizie, octet, și întreg scurt. Toate tipurile numerice sunt cu semn. Întregul scurt fără semn există doar pentru a folosi la caracterele Unicode. Aceste tipuri primitive de date sunt tratate de către compilator și nu de programul Java compilat, sau mediul de execuție Java. Instrucțiunile Java conțin cod operație diferit pentru diferitele tipuri de date primitive (ex: iadd, ladd, fadd și dadd pentru adunarea a două numere). RegiștriRegiștrii Mașinii Virtuale Java păstrează starea acesteia în timpul operațiilor,
la fel ca și regiștrii microprocesoarelor. Mașina Virtuală Java are patru
regiștrii: Mașina Virtuală Java definește fiecare registru ca fiind de 32 biți. Anumite implementări pot să nu utilizeze toți regiștrii (ex. Dacă se generează cod nativ, atunci nu mai avem nevoie de pc). Mașina virtuală Java este bazată pe stivă, adică nu folosește regiștrii pentru transferuri de parametrii, aceasta fiind o decizie luată în favoarea simplității codului și, totodată, permite implementare eficientă pe diferite procesoare gazdă, care au un număr redus de regiștrii, cum ar fi cele din familia x86. Stiva JavaMașina virtuală Java folosește stiva pentru a furniza parametrii pentru
operații, pentru a prelua rezultatele acestora, pentru a transfera parametrii
metodelor, etc. O fereastră de stiva (stack frame) Java este echivalentul
unei ferestre de stivă pentru un limbaj de programare convențional. O fereastră
de stivă are trei componente: Spațiul alocat variabilelor locale și mediului de execuție este fixat la apelul metodei, în timp ce stiva de operanzi se modifică în timpul execuției metodei. Variabilele locale sunt stocate într-un vector și sunt adresate de către registrul vars. Variabilele locale sunt stocate pe 32 biți. Tipul long și double sunt considerate ca fiind formate din două variabile locale. Variabilele locale se încarcă în stiva de operanzi, iar instrucțiunile vor păstra valorile acestora în spațiul variabilelor locale. Mediul de execuție este componenta din fereastra de stivă, care se folosește pentru a păstra operațiile stivei Java. Conține pointeri către fereastra de stivă precedentă, precum și către baza și vârful stivei de operanzi și variabile locale. Mai conține informații suplimentare aparținând mediului de execuție (pentru depanare). Excepții. Fiecare metodă Java conține o serie de clauze catch. Fiecare clauză catch descrie domeniul pentru care este activă, tipul excepției care va fi tratată și are un bloc de tratare a excepției. Când se aruncă o excepție, aceasta este căutată în lista catch. O clauză cath este potrivită (pentru tratarea excepției) dacă instrucțiunea care a aruncat excepția se află în domeniul pentru care este activă clauza catch respectivă și excepția aruncată este derivată din cea pe care o tratează clauza catch. Dacă se găsește o clauză catch potrivită, sistemul va comuta la codul de tratare a excepției. Dacă nu se găsește nici o clauză potrivită, se părăsește fereastra de stivă actuală (metoda actuală) și excepția este aruncată mai departe, către un nivel superior celui în care a apărut. Ordinea clauzelor catch este importantă, interpretorul executând codul de tratare a primei clauze care se potrivește. Stiva de operanzi. Este o stivă FIFO pe 32 biți. Ea folosește pentru a stoca argumentele și rezultatele mai multor instrucțiuni ale mașinii virtuale Java. De exemplu operația iadd adună două numere întregi. Operația citește două numere întregi (32 biți) din vârful stivei, le adună, iar rezultatul este stocat tot în stiva de operanzi. Operația iadd presupune că o altă instrucțiune a mașinii virtuale a pus în vârful stivei cele două valori care trebuie să fie adunate și că rezultatul va fi preluat din stiva de operanzi. Pentru fiecare tip de date, există operații specializate pentru acel tip, este ilegal ca să se depună pe stivă două numere întregi și apoi să fie citite ca un întreg lung (64 biți). Rezerva de constante (Constant pool)Fiecărei clase Java i se asociază o rezervă de constante, care va păstra numele tuturor câmpurilor, metodelor și a tuturor informațiilor care pot fi utilizate de oricare metodă a clasei. La încărcarea unei clase Java se va citi lungimea rezervei de constante (constant_pool_count), după care va urma efectiv spațiul alocat acesteia, începând cu constant_pool[0] și până la constant_pool[constant_pool_count-1]. Fiecare element (constant_pool[i]) are următoarea structură: cp_info { u1 tag; u1 info[]; } Înregistrările încep cu o etichetă de un octet, care indică tipul înregistrării. De exemplu, pentru descriptorul unei clase vom avea valoarea CONSTANT_Class = 7. Formatul informației care urmează după etichetă diferă în funcție de ceea ce reprezintă înregistrarea. De exemplu pentru o clasă avem: CONSTANT_Class_info { u1 tag; u2 name_index; } unde tag= CONSTANT_Class (7) iar name _index reprezintă un index în constant_pool care va indica spre o înregistrare CONSTANT_Utf8_info, care va reprezenta numele clasei. Colectorul de deșeuriMașina virtuală Java mai include un colector de deșeuri. Acesta este un proces care rulează în umbră și el este răspunzător de elibararea resurselor ocupate de programe. Există mai multe tehnici pentru realizarea acestuia, dar nu se impune implementarea uneia dintre acestea, cel care realizează o Mașină virtuală Java poate să implementeze propriile tehnici care să realizeze acest lucru. În Java nu se permite programatorului să aibă acces direct la resurse, de ocuparea și eliberarea propriu zisă a lor se ocupă mediul de execuție. De obicei, când o resursă nu mai este referită de nimeni, ea va fi eliberată de către colectorul de deșeuri. Implementarea acestor elemente ale mașinii virtuale Java nu impune nici o restricție privitoare la tehnica care se folosește. Diferitele implementări ale mașinii virtuale Java comunică prin intermediul fișierelor .class. Mașina virtuală recunoaște aceste fișiere și nu limbajul Java. Astfel, se pot realiza compilatoare care să genereze cod binar Java și pentru alte limbaje, singura condiție fiind să se genereze un fișier .class care are structura celui recunoscut de mașina virtuală. Structura fișierului .classFișierele .class păstrează codul compilat pentru clasele și interfețele Java. Acest fișier are structura prezentată în figura Structura fișierelor .class". În această figură u1,u2,u4 reprezintă valori fără semn, având lungimea de 1, 2 respectiv 4 octeți. Elementele din structura fișierului .class sunt: Fișierul Hello.classPentru exemplificarea structurii unui fișier class am ales fișierul Hello.class, care se generează din următorul fișier sursă (Hello.java): class Hello{ public static void main(String args[]){ System.out.println("Hello World"); } } Dacă vizualizăm conținutul fișierului Hello.class cu un editor, care este capabil să arate și în hexazecimal (wpwiev din NC), putem citi structura din figura Hello.class". Referințe: BYTE România - septembrie 1997
(C) Copyright Computer Press Agora |