620 likes | 794 Views
Java Virtual Machine. Specyfikacja - najważniejsze zagadnienia. XI.2003 Mirosław Szymański Wydział Matematyki, Informatyki i Mechaniki Uniwersytetu Warszawskiego. Spis treści. Wstęp Struktura JVM Instrukcje wirtualnej maszyny Pliki class Ładowanie, łączenie, inicjalizacja
E N D
Java Virtual Machine Specyfikacja - najważniejsze zagadnienia XI.2003 Mirosław Szymański Wydział Matematyki, Informatyki i Mechaniki Uniwersytetu Warszawskiego
Spis treści • Wstęp • Struktura JVM • Instrukcje wirtualnej maszyny • Pliki class • Ładowanie, łączenie, inicjalizacja • Wątki i blokady • Kompilacja dla wirtualnej maszyny • Literatura
Bytecode • Zagadka: co robi poniższy program Method void main(java.lang.String[]) 0 getstatic #2 3 ldc #3 5 invokevirtual #4 8 return
Wstęp – historia Javy • Cel powstania (początek prac – 1991) • tworzenie oprogramowania dla urządzeń pracujących w sieci • Wymagania / konsekwencje • wspomaganie wielu architektur • bezpieczeństwo • krótki kod • Pierwsza przeglądarka: HotJava (1994)
Wirtualna Maszyna Javy • Wirtualny komputer posiadający: • pamięć • zestaw instrukcji (assembler ~ bytecode) • wewnętrzne typy danych • Specyfikacja definiuje tylko to co niezbędne • format plików class • instrukcje
Typy podstawowe (primitive) • numeryczne • całkowite (integral) • byte(8-bit), short(16-bit), int(32-bit), long(64-bit) • char(16-bit) • zmienno-przecinkowe (floating-point) • float(32-bit), double(64-bit) • boolean • true / false • returnAddress • wskaźnik do instrukcji JVM • używany przez jsr (jump subroutine), ret, jsr_w • jedyny typ nie posiadający swojego odpowiednika w jęz. Java
Typy podstawowe (c.d.) • byte, short, int, long– ze znakiem, dopełnienie do dwóch • char– bez znaku, Unicode • float, double– zgodne z IEEE 754 • +0.0, -0.0 • +∞, -∞ • +0.0 == -0.0ale1.0/0.0 = +∞oraz1.0/-0.0 = -∞ • NaN – Not a Number • reprezentuje wynik błędnej operacji takiej jak dzielenie przez zero (?) • w przeciwieństwie do wszystkich innych wartości jest nieuporządkowany(wszystkie porównania dowolnej wartości z NaN dają false;NaN≠NaN) • boolean – wyrażenia kompilowane są tak, aby używały typu int • tablice typu boolean– dostęp przezbaloadorazbastore • w implementacji Sun’a boolean zajmuje 8 bitów
Typy referencyjne • Wartościami są wskaźniki do obiektów • Dzielimy na typy: • klasowe (classtypes) • tablicowe (arraytypes) • interfejsów (interfacetypes) • Specjalna wartość: null • nie jest żadnego określonego typu (ale może być rzutowany na dowolny typ) • specyfikacja JVM nie określa żadnej konkretnej wartości kodującej null
Obszary danych czasu wykonania • Osobne dla każdego wątku • rejestr pc(program counter) • stos JVM (JVM stack) • ramki • stos metod typu native • Współdzielone przez wątki • obszar metod (method area) • constant pool • sterta (heap)
Rejestrpc(program counter) • wskaźnik (indeks) do bieżącej instrukcji • każdy wątek posiada własny rejestr pc • dla metod native wartość jest niezdefiniowana
Stos JVM • każdy wątek posiada prywatny stos • tworzony w tym samym czasie, co wątek • analogiczny do stosu z języka C • zmienne, częściowe wyniki, wywoływanie metod i powroty • pamięć obszaru nie musi być ciągła • rozmiar stały lub zmieniany dynamicznie • Sun: pamięć nieciągła, powiększany ale nie zmniejszany, maksymalny rozmiar – parametr „-oss”; ograniczenie na zużycie pamięci, wykrywanie nieskończonych rekursji. • sytuacje wyjątkowe: • podczas wykonania: StackOverflowError • podczas tworzenia wątku: OutOfMemorError
Stos metod typu native • rozmiar stały lub zmieniany dynamicznie • Sun: rozmiar stały – parametr „-ss”, nie sprawdza przepełnienia stosu • sytuacje wyjątkowe • StackOverflowError • OutOfMemoryError
Sterta (heap) • dzielona przez wszystkie wątki • pamięć na instancje klas i tablic • tworzona podczas uruchamiania JVM • zarządzany przez garbage collector * • obiekty nigdy nie są jawnie dealokowane • pamięć obszaru nie musi być ciągła • rozmiar stały lub zmieniany dynamicznie • Sun: rozmiar zmienny, powiększany dynamicznie ale nie zmniejszany; rozmiar początkowy i max. – „-ms” i „-mx” • sytuacje wyjątkowe: • OutOfMemoryError
Obszar metod (method area) • dzielony przez wszystkie wątki • przechowuje: runtime constant pool, dane klas (metody, kod metod, …) • logicznie część sterty, ale twórca VM może nie chcieć żeby obszar ten był zarządzany przez garbage collector • pamięć obszaru nie musi być ciągła • rozmiar stały lub zmieniany dynamicznie • Sun: obsługiwany przez GC, kontrola rozmiaru min, max i init. • sytuacje wyjątkowe: • OutOfMemoryError
Constant pool • posiadany przez wszystkie klasy i interfejsy • reprezentuje tablicę constant_pool z pliku class • używany przy dynamicznym linkowaniu (dynamic linking) • odpowiednik tablicy symboli w konwencjonalnych językach, jednak zawiera więcej danych • alokowany z obszaru metod • sytuacje wyjątkowe: • StackOverflowError, OutOfMemoryError
Ramki • tworzone w momencie wywołania metody • alokowane ze stosu JVM • każda ramka posiada • tablice zmiennych lokalnych • operand stack • referencję do constant pool klasy metody
Ramki – tablica zmiennych lokalnych • pojedyncza zmienna może być typu: • boolean, byte, char, short, int,float, reference, returnAddress • para zmiennych może przechowywać wartości typów: • long, double • wartości long, double nie muszą być wyrównywane do 64-bitów • wykorzystywane do przekazywania parametrów do metod • specjalne znaczenie pozycji nr 0
Ramki – operand stack • służy do • przekazania parametrów dla instrukcji JVM • zwracania wyników instrukcji JVM • zwracania wyników przez metody • np.: iadd …, value1, value2 → …, result • wartości na stosie muszą być odpowiednich typów (sprawdzane podczas weryfikacji plików class) iadd
Ramki (c.d.) • zakończenie wywołania metody: • normalne • nie wystąpił wyjątek • zostaje zwrócona wartość do metody wywołującej (operand stack) • nienormalne, niespodziewane (abrupt) • wystąpił wyjątek nie obsługiwany przez tą metodę • wynik nie zostaje zwrócony
Inne ustalenia • reprezentacja obiektów • arytmetyka zmienno-przecinkowa • specjalnie nazwane metody inicjalizacyjne • nazwy wspomagane przez kompilator (nie są prawidłowymi identyfikatorami) • <init> • odpowiednik konstruktora • instrukcja wywołująca: invokespecial • <cinit> • metoda inicjalizująca klasę lub interfejs • statyczna, bez argumentów • wywoływana bezpośrednio przez JVM, nigdy przez instrukcję JVM
Wyjątki • podniesienie wyjątku powoduje natychmiastową zmianę miejsca wykonania • przerywane jest działanie kolejnych metod, do momentu znalezienia klauzuli catch. Jeśli nie ma odpowiedniego catch to wątek kończy działanie. • jeśli są klauzule finally, to są one wykonywane • klauzule catch oraz finally reprezentowane są przez exception handler
Wyjątki – exception handlers • przechowywane są w tablicy (tablica zapisana jest w pliku class) • pojedynczy exception handler (EH): • określa zakres instrukcji dla których jest aktywny • określa typ obsługiwanego wyjątku • zawiera adres kodu obsługującego ten wyjątek • kolejność EH w tablicy jest ważna • tablica przeglądana jest liniowo
Instrukcje • kod instrukcji ma 1 bajt • rożne instrukcje dla poszczególnych typów • np.: iload ładuje na stos zmienną typu int, fload - typu float • zestaw instrukcji nie jest ortogonalny
Instrukcje (c.d.) • rodzaje instrukcji • load oraz store iload, iload_<n>, fload, iconst_<n>, … • arytmetyczne iadd, dsub, … • konwersja typów i2l, i2f, i2d, l2f, l2d, f2d; i2b, i2c, i2s, … • operacje na obiektach new, newarray, arraylength, instanceof, … • operacje na stosie arg. pop, dup, swap, … • instrukcje skoku ifeq, ifnull, tableswitch, goto, jsr, … • metody invokevirtual, return, ireturn, … • podnoszenie wyjątków athrow • synchronizacja monitorenter, monitorexit
Pliki class • Typy danych: u1, u2, u4 • Tablice • tables – zawierają elementy zmiennych rozmiarów (tablica (?)) • arrays – zawierają elementy stałych rozmiarów (tabela (?)) • struktury opisane za pomocą struktur z języka C
Pliki class(c.d.) • Plik class składa się z jednej struktury ClassFile: ClassFile { u4 magic; // identyfikuje pliki class (0xCAFEBABE) u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; // ACC_PUBLIC, ACC_FINAL, ACC_SUPER, // ACC_INTERFACE, ACC_ABSTRACT u2 this_class; // indeks do tablicy constant_pool u2 super_class; u2 interfaces_count; // liczba implementowanych interfejsów u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; // reprezentuje wszystkie zadeklarowane metody // (nie zawiera metod odziedziczonych) u2 attributes_count; attribute_info attributes[attributes_count]; //SourceFile, Deprecated //InnerClasses(?) }
Pliki class(c.d.) • Deskryptory pól FieldDescriptor→ FieldType ComponentType → FieldType FieldType→ BaseType | ObjectType | ArrayType BaseType → B | C | D | F | I | J | S | Z ObjectType → L<classname>; ArrayType → [ComponentType B – byte; C – char; D– double; F – float; I – int; J – long; S – short; Z – boolean np.: doubletablica[][][]→[[[D
Pliki class(c.d.) • Deskryptory metod MethodDescriptor→ (ParameterDescriptor*) ReturnDescriptor ParameterDescriptor → FieldType ReturnDescriptor → FieldType | V V – void np.: ObjectmyMethod(int i,double d,Thread t) (IDLjava/lang/Thread;)Ljava/lang/Object;
Pliki class(c.d.) • Tablica constant_pool zawiera pola typu: • CONSTANT_Class • CONSTANT_Fieldref • CONSTANT_Methodref • CONSTANT_InterfaceMethodref • CONSTANT_String • CONSTANT_Integer • CONSTANT_Float • CONSTANT_Long • CONSTANT_Double • CONSTANT_NameAndType • CONSTANT_Utf8
Pliki class(c.d.) • Pola (fields) field_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_cout; attribute_info attributes[attributes_count]; } access_flags: ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT attributes[]: m.in.: ConstantValue, Deprecated
Pliki class(c.d.) • Metody method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_cout; attribute_info attributes[attributes_count]; } access_flags: ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT attributes[]: m.in.:Code, Exceptions, Synthetic, Deprecated
Pliki class(c.d.) • Atrybut Code Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; }
Ograniczenia na kod JVM • Statyczne, m.in.: • tablica z kodem musi być niepusta • jedyne dozwolone instrukcje, to te wymienione w specyfikacji • każda instrukcja ze skokiem musi wskazywać na kod instrukcji wewnątrz danej metody • instrukcja anewarray może być używana do tworzenia tablic o maksymalnym wymiarze 255 • …
Ograniczenia na kod JVM (c.d.) • Strukturalne, m.in.: • każda instrukcja może być wykonana tylko z odpowiednią liczbą argumentów i z odpowiednimi typami tych argumentów • jeśli instrukcja może być wykonana w skutek różnych ścieżek wykonania, to stos argumentów (operand stack) musi mieć tą samą głębokość niezależnie od ścieżki • każda instrukcja return musi odpowiadać typowi zwracanemu przez daną metodę • …
Weryfikacja plików class • JVM nie może zakładać poprawności plików class • Co jest sprawdzane: • 0 ≤ rozmiar stosu ≤ max_rozmiar • wszystkie zmienne lokalne są używane prawidłowo • argumenty instrukcji są odpowiednich typów • klasy final nie posiadają podklas • każda klasa (oprócz Object) posiada bezpośrednią nadklasę • …
Ładowanie, łączenie, inicjalizacja • Ładowanie (loading) • proces wyszukiwania binarnej reprezentacji klasy lub interfejsu i utworzenie klasy/interfejsu • klasy nie będące tablicami ładowane są przez class loader’a • programista może zdefiniować własny class loader • Łączenie • weryfikacja • „umiejscowienie” danej klasy/interfejsu w aktualnym stanie Wirtualnej Maszyny
Wątki i blokady (locks) • Niskopoziomowe mechanizmy korzystania przez wątki z pamięci dzielonej • Pamięć: • pamięć główna współdzielona przez wątki • pamięć robocza wątku • Pamięć główna zawiera kopie główne zmiennych • Pamięć robocza zawiera kopie robocze zmiennych • Pamięć główna: jeden obiekt – jedna blokada • Akcje wykonywane przez pamięć główną: • read, write, lock, unlock • Akcje wykonywane wątek: • use, assign, load, store, lock, unlock • Powyższe akcje są atomowe.
Wątki i blokady (locks) • read: (pamięć gł.) kopia główna → pamięć robocza • load: (wątek) pamięć robocza → kopia robocza • use: (wątek) kopia robocza→thread’s execution engine • assign: (wątek) thread’s e.e. → kopia robocza • store: (wątek) kopia robocza → pamięć główna • write: (pamięć gł.) pamięć główna → kopia główna • lock: (wątek, operacja ściśle zsynchronizowana z pamięcią główną) • unlock: (wątek, operacja ściśle zsynchronizowana z pamięcią główną)
Operand stack i zmienne lokalne iload_0 // push the int in local variable 0 iload_1 // push the int in local variable 1 iadd // pop two ints, add them, push result istore_2 // pop int, store into local variable 2
Stałe, zmienne lokalne, instrukcje skoku void spin() { int i; for (i = 0; i < 100; i++) { ;// Loop body is empty } } Method void spin() 0 iconst_0 // Push int constant 0 1 istore_1 // Store into local variable 1 (i=0) 2 goto 8// First time through don't increment Loop body 5 iinc 1 1 // Increment local variable 1 by 1 (i++) 8 iload_1 // Push local variable 1 (i) 9 bipush 100 // Push int constant 100 11 if_icmplt 5// Compare and loop if less than (i < 100) 14 return // Return void when done
Operacje arytmetyczne, parametry metod int align2grain(int i, int grain) { return ((i + grain-1) &~(grain-1)); } Method int align2grain(int,int) 0 iload_1 // Push i 1 iload_2 // Push grain 2 iadd 3 iconst_1 4 isub 5 iload_2 // Push grain 6 iconst_1 7 isub 8 iconst_m1 9 ixor // (~x == -1^x) 10 iand 11 ireturn
Wywoływanie metod int addTwo(int i, int j) { return i + j; } int add12and13() { returnaddTwo(12,13); } Method int add12and13() 0 aload_0 // Push local variable 0 (this) 1 bipush 12// Push int constant 12 3 bipush 13// Push int constant 13 5 invokevirtual #4// Method Example.addtwo(II)I 8ireturn // Return int on top of operand stack; it is // the int result of addTwo()