JVM(Java Virtual Machine) działa jako silnik run-time do uruchamiania aplikacji Java. JVM jest tym, który faktycznie wywołuje główną metodę obecną w kodzie java. JVM jest częścią JRE (Java Runtime Environment).
Aplikacje Java są nazywane WORA (Write Once Run Anywhere). Oznacza to, że programista może tworzyć kod Java na jednym systemie i oczekiwać, że będzie on działał na każdym innym systemie obsługującym Javę bez żadnych poprawek. To wszystko jest możliwe dzięki JVM.
Kiedy kompilujemy plik .java, pliki .class (zawierają kod bajtowy) z tymi samymi nazwami klas, które są obecne w pliku .java są generowane przez kompilator Javy. Ten plik .class przechodzi przez różne kroki, kiedy go uruchamiamy. Te kroki razem opisują całą maszynę JVM.
Podsystem Ładowacza Klas
Odpowiada on głównie za trzy czynności.
- Ładowanie
- Podlinkowanie
- Inicjalizacja
Ładowanie: Program ładujący klasy odczytuje plik „.class”, generuje odpowiednie dane binarne i zapisuje je w obszarze metody. Dla każdego pliku „.class”, JVM przechowuje następujące informacje w obszarze metod.
- W pełni kwalifikowana nazwa załadowanej klasy i jej bezpośredniej klasy nadrzędnej.
- Czy plik „.class” jest związany z klasą lub interfejsem lub enum.
- Informacje o modyfikatorach, zmiennych i metodach itp.
Po załadowaniu pliku „.class”, JVM tworzy obiekt typu Class, aby reprezentować ten plik w pamięci sterty. Proszę zauważyć, że ten obiekt jest typu Class predefiniowany w pakiecie java.lang. Ten obiekt klasy może być użyty przez programistę do uzyskania informacji na poziomie klasy, takich jak nazwa klasy, nazwa rodzica, metody i informacje o zmiennych itp. Aby uzyskać referencję do tego obiektu możemy użyć metody getClass() klasy Object.
import
java.lang.reflect.Field;
import
java.lang.reflect.Field;
import
java.lang.reflect.Method;
public
class
Test {
public
static
void
main(String args)
{
Student s1 =
new
Student();
Class c1 = s1.getClass();
System.out.println(c1.getName());
Method m = c1.getDeclaredMethods();
for
(Method method : m)
System.out.println(method.getName());
Field f = c1.getDeclaredFields();
for
(Field field : f)
System.out.println(field.getName());
}
}
class
Student {
private
String name;
private
int
roll_No;
public
String getName() {
return
name; }
public
void
setName(String name) {
this
.name = name; }
public
int
getRoll_no() {
return
roll_No; }
public
void
setRoll_no(
int
roll_no)
{
this
.roll_No = roll_no;
}
}
}
StudentgetNamesetNamegetRoll_nosetRoll_nonameroll_No
Uwaga: Dla każdego załadowanego pliku „.class” tworzony jest tylko jeden obiekt danej klasy.
Student s2 = new Student();// c2 will point to same object where // c1 is pointingClass c2 = s2.getClass();System.out.println(c1==c2); // true
Linkowanie: Wykonuje weryfikację, przygotowanie i (opcjonalnie) rozwiązanie.
- Weryfikacja: Zapewnia poprawność pliku .class tzn. sprawdza czy ten plik jest poprawnie sformatowany i wygenerowany przez poprawny kompilator czy nie. Jeśli weryfikacja nie powiedzie się, otrzymujemy wyjątek java.lang.VerifyError. Czynność ta jest wykonywana przez komponent ByteCodeVerifier. Gdy ta czynność zostanie zakończona, plik klasy jest gotowy do kompilacji.
- Przygotowanie: JVM alokuje pamięć dla zmiennych klasowych i inicjalizuje pamięć do wartości domyślnych.
- Rozwiązanie: Jest to proces zastępowania symbolicznych referencji z typu referencjami bezpośrednimi. Odbywa się to poprzez przeszukiwanie obszaru metody w celu zlokalizowania odwołującej się jednostki.
Inicjalizacja: W tej fazie wszystkie zmienne statyczne są przypisywane do ich wartości zdefiniowanych w kodzie i bloku statycznym(jeśli istnieje). Jest to wykonywane od góry do dołu w klasie i od rodzica do dziecka w hierarchii klas.
Ogólnie, istnieją trzy ładowacze klas:
- Ładowacz klasy Bootstrap: Każda implementacja JVM musi posiadać bootstrap class loader, zdolny do ładowania zaufanych klas. Ładuje on podstawowe klasy API java znajdujące się w katalogu „JAVA_HOME/jre/lib”. Ta ścieżka jest popularnie znana jako ścieżka bootstrap. Jest ona zaimplementowana w językach natywnych takich jak C, C++.
- Ładowacz klas rozszerzeń: Jest to dziecko programu ładującego klasy bootstrap. Ładuje klasy znajdujące się w katalogach rozszerzeń „JAVA_HOME/jre/lib/ext” (ścieżka rozszerzeń) lub w jakimkolwiek innym katalogu określonym przez właściwość systemową java.ext.dirs. Jest on implementowany w języku java przez klasę sun.misc.Launcher$ExtClassLoader.
- Systemowy/aplikacyjny class loader: Jest dzieckiem programu ładującego klasy rozszerzeń. Jest odpowiedzialny za ładowanie klas ze ścieżki klas aplikacji. Wewnętrznie używa zmiennej środowiskowej, która jest odwzorowana na java.class.path. Jest on również zaimplementowany w Javie przez klasę sun.misc.Launcher$AppClassLoader.
public
class
Test {
public
static
void
main(String args)
{
System.out.println(String.
class
.getClassLoader());
System.out.println(Test.
class
.getClassLoader());
}
}
nulljdk.internal.loader.ClassLoaders$AppClassLoader@8bcc55f
Uwaga: JVM podąża za zasadą Delegacja-Hierarchia, aby załadować klasy. Systemowy program ładujący klasy deleguje żądanie załadowania do programu ładującego klasy rozszerzeń, a program ładujący klasy rozszerzeń deleguje żądanie do programu ładującego klasy bootstrap. Jeśli klasa została znaleziona w ścieżce bootstrap, jest ona ładowana, w przeciwnym razie żądanie jest ponownie przekazywane do loadera klas rozszerzeń, a następnie do loadera klas systemowych. W końcu, jeśli systemowy program ładujący klasy nie zdoła załadować klasy, wówczas otrzymujemy wyjątek java.lang.ClassNotFoundException.
Pamięć JVM
- Obszar metod: W obszarze metod przechowywane są wszystkie informacje na poziomie klasy, takie jak nazwa klasy, nazwa bezpośredniej klasy nadrzędnej, metody i informacje o zmiennych itp. w tym zmienne statyczne. Istnieje tylko jeden obszar metod na JVM, i jest to zasób współdzielony.
- Obszar sterty: Informacje o wszystkich obiektach są przechowywane w obszarze sterty. Istnieje również jeden obszar sterty na maszynę JVM. Jest to również zasób współdzielony.
- Obszar stosu: Dla każdego wątku, JVM tworzy jeden stos run-time, który jest przechowywany tutaj. Każdy blok tego stosu jest nazywany rekordem aktywacji / ramką stosu, która przechowuje wywołania metod. Wszystkie zmienne lokalne tej metody są przechowywane w odpowiadającej im ramce. Po zakończeniu wątku, jego stos run-time zostanie zniszczony przez JVM. Nie jest on zasobem współdzielonym.
- Rejestry PC: Przechowują adres bieżącej instrukcji wykonania wątku. Oczywiście, każdy wątek ma oddzielne rejestry PC.
- Stosy metod natywnych: Dla każdego wątku tworzony jest osobny stos natywny. Przechowuje on informacje o natywnych metodach.
Motor egzekucyjny
Motor egzekucyjny wykonuje „.class” (bytecode). Odczytuje on bajt-kod linia po linii, wykorzystuje dane i informacje obecne w różnych obszarach pamięci i wykonuje instrukcje. Może być sklasyfikowany na trzy części:
- Interpreter: Interpretuje bytecode linia po linii, a następnie wykonuje. Wadą tego rozwiązania jest to, że gdy jedna metoda jest wywoływana wiele razy, za każdym razem wymagana jest interpretacja.
- Kompilator Just-In-Time(JIT): Jest używany do zwiększenia wydajności interpretera. Kompiluje cały kod bajtowy i zmienia go na kod natywny, więc za każdym razem, gdy interpreter widzi powtarzające się wywołania metod, JIT zapewnia bezpośredni kod natywny dla tej części, więc ponowna interpretacja nie jest wymagana, więc wydajność jest poprawiona.
- Garbage Collector: Niszczy obiekty, do których nie ma odniesienia. Więcej na temat Garbage Collector, patrz Garbage Collector.
Java Native Interface (JNI):
Jest to interfejs, który współdziała z Native Method Libraries i dostarcza natywne biblioteki (C, C++) wymagane do wykonania. Umożliwia on JVM wywoływanie bibliotek C/C++ oraz bycie wywoływanym przez biblioteki C/C++, które mogą być specyficzne dla sprzętu.
Natywne biblioteki metod :
Jest to zbiór bibliotek natywnych (C, C++), które są wymagane przez silnik wykonawczy.
Ten artykuł został napisany przez Gaurav Miglani. Jeśli podoba Ci się GeeksforGeeks i chciałbyś się do niego przyczynić, możesz również napisać artykuł używając contribute.geeksforgeeks.org lub wysłać swój artykuł na adres [email protected]. Zobacz, jak twój artykuł pojawia się na stronie głównej GeeksforGeeks i pomóż innym Geekom.
Proszę pisać komentarze, jeśli znajdziesz coś niepoprawnego lub chcesz podzielić się więcej informacji na temat poruszony powyżej.