A JVM működése – avagy a Java és a platformfüggetlenség
Cikkünkben beszélünk a platformfüggetlenségről általánosságban, majd megnézzük, hogy a Java vajon mennyiben felel meg ezeknek a kritériumoknak, illetve, hogy ennek a platformfüggetlenségnek mik a potenciális korlátai a Java esetében.
A legutolsó szekcióban az úgynevezett JVM működése lesz a fő témakör, és a vele kapcsolatos témákat fogjuk közösen átgondolni.
Mi az a platformfüggetlenség?
A platformfüggetlenség röviden azt jelenti, hogy az írt szoftverünk – minimális vagy semmilyen módosítással – több számítógépes platformon is képes működni.
Röviden tehát, ha az alkalmazásunk platformfüggetlen, akkor különböző operációs rendszereken (Linux, Windows, …) képes futni gyakorlatilag módosítás nélkül. Másképp megfogalmazva, egy programozási nyelvet akkor hívunk platformfüggetlennek, ha az általa írt programokra elmondható az, hogy többplatformosak, tehát, hogy az alkalmazások jelentős módosítás nélkül futnak különböző operációs rendszereken és architektúrákon.
A Java platformfüggetlen?
Gyakori interjúkérdés, hogy a Java platformfüggetlen nyelv-e. A kérdés megválaszolásához át kell látnunk, hogy pontosan mi lesz a Java kódunkkal, amíg ténylegesen program lesz belőle. Nézzük meg egy Java Class útját a kezdetektől:
- Megírjuk a fájlt .java kiterjesztésben. Itt az adott Java verzió szintaxisának megfelelő kódot használjuk.
- A javac fordító segítségével ebből bytecode-ot generálunk. Ez a bytecode már .class kiterjesztésű. A bytecode-ról itt olvashatsz bővebben.
- Minden operációs rendszerre írtak egy a rendszertől függő Java virtuális gépet (angolul: Java Virtual Machine, tehát JVM). A JVM működése a következő alfejezet témája lesz, de a lényeg az, hogy a bytecode-ot gépi kódra fordítja, amit a célplatform képes futtatni.
A fentieket a következő fejezetben, melynek témája a JVM működése, jobban kifejtem, de már ebből is megkaptuk a választ: a Java platformfüggetlen nyelv, mert bármilyen olyan platformon lehet futtatni a Java szintaxisa alapján megírt kódot, amelyre készült JVM.
A JVM működése
Mint ahogy az előző bejegyzésben már említettem, a JVM egy virtuális gép, melyet az adott platformra fejlesztenek azért, hogy az képes legyen a javac által lefordított .class fájlokat lefordítani úgy, hogy azokból az adott platformon értelmezhető utasításokat kapjunk.
Éppen ezért mondtuk azt, hogy a Java platformfüggetlen, hiszen bármilyen platformon lefuttatható a Java-ban írt kód, ha van rajta JVM, viszont fontos hogy ettől még maga a JVM nem lesz platformfüggetlen. Mit is jelent ez?
Ördöngösen hangzik, de ez csak annyit jelent, amit már korábban is megállapítottunk: minden platformra egyedi JVM-et kell írni, és egy Android-os rendszerre írt JVM eltér egy Windows-ra írttól. Pont a JVM teszi lehetővé azt, hogy a Java platformfüggetlen nyelv legyen, viszont ahhoz előtte valakiknek meg kell írnia a JVM-et az adott platformra. A platformfüggőség hozzájárul a Java töretlen népszerűségéhez is.
A JVM működése mélyebben – a név elemzése
A JVM működése egy eszméletlenül izgalmas témakör. Ahhoz, hogy mélyebben megértsük ennek a működésnek a jellegét, először nézzük meg, hogy maga a Java Virtual Machine kifejezés mire utal. A virtual machine (virtuális gép) elnevezés egy fizikai gép szoftveres szimulációjára utal. A virtuális gépek lehetnek hardver-és rendszeralapúak vagy szoftveres alapúak.
A JVM egy olyan szoftveres alapú virtuális gép, amely engine-ként szolgál ahhoz, hogy az adott platformon futtatni lehessen a Java-ban írt szoftverünket.
A JVM-re tehát tekintsünk úgy, mint egy virtuális gépre, amin rengeteg dolog testreszabható. De mit is tartalmaz ez a virtuális gép?
A Java virtuális gép elemei
A következőkben a teljesség igénye nélkül felsorolom a Java virtuális gép elemeit, illetve nagyvonalakban leírom, hogy miért van rájuk szükség.
1. Class loader-ek:
Az osztályok betöltéséért felelnek. Van három előre definiált class loader, de szükség esetén sajátot is lehet írni a ClassLoader class extend-álásával (ha a mondat második fele kínai, nyugodtan menj tovább!).
2. Method area:
A JVM többek közt itt tárolja a metódusok kódjait.
3. Heap:
A heap az a memóriaterület, ahol a referenciatípusokat tárolja a virtuális gép.
4. Stack:
A stack-en a primitív típusok kerülnek tárolásra. Ha nem tudod, mik azok a primitív-és referenciatípusok, látogass el ide.
5. Program counter regiszterek:
Minden egyes ilyen regiszter azonosítja azokat a műveleteket, amik épp végrehajtásra kerülnek.
6. Native method stack:
A natív java metódusok vannak itt. Na de mik is azok a natív metódusok? Ezt a következő alfejezetben kifejtem!
7. Execution engine:
Ez a végrehajtó környezet, mely a következő elemeket tartalmazza:
- Egy virtuális processzor
- Interpreter: értelmezi, és végrehajtja a byte code-ból kiolvasott instrukciókat.
- Egy úgynevezett JIT compiler (Just-In-Time fordító), ami a byte code-ból gépi kódot generál. A hasonló funkcionalitású kódokat egyszerre tudja lefordítani, ezért nagyon gyors (innen az elnevezése).
8. Java Native Interface
Kis érdekesség – a natív metódusok
A natív metódusok (angolul: native methods) egyéb nyelven (többnyire C-ben vagy C++ – ban) megírt metódusok. Ezeket viszonylag ritkán kell használnunk – nekem eddig soha nem kellett -, de néhány esetben indokolt lehet a használatuk. Ezek közül gyűjtöttem össze néhányat:
- Olyan könyvtárakat akarunk használni, amik más programnyelvekben íródtak.
- Már megírt C/C++ kódot akarunk újrafelhasználni a Java programunkban.
- Olyan hardverelemekhez szeretnénk közvetlen hozzáférést, amikhez C-ben hozzá lehet férni, de Java-ban nem.
- A Java-ban megírt algoritmus valamilyen okból túl lassúnak bizonyul, ezért alacsonyabb szintű programozási nyelvhez nyúlunk a hatékonyság növelése érdekében.
Érdekesség, hogy maga a Java virtuális gép is tartalmaz natív metódusokat, amik ahhoz kellenek, hogy maga a VM működjön.
JVM, JRE és JDK
Röviden összefoglalva:
A JVM egy virtuális gép, ami futtatni tudja a Java programunkat.
A JRE egy futtatási környezet vagy konténer, amin belül a JVM fut.
A JDK egy fejlesztői csomag, ami magában foglalja a JRE-t, és ezen kívül rengeteg olyan eszközt használ, amit fejlesztők napi szinten használnak. Ezek közül néhány:
- Java debugger – ennek segítségével tudjuk lépésről lépésre megnézni a futtatott programunkban, hogy mi lehet egy esetleges hiba oka.
- Java compiler (javac) – ez a fordító; mint az első ábrán is szerepel, ennek segítségével lehet a Java kódból ún. byte code-ot előállítani, amiből a Java virtuális gép gépi kódot generál.
- Javadoc – dokumentáció-generáló eszköz, ami HTML-dokumentációt generál. A dokumentáció arra alkalmas, hogy leírjuk, hogy egy adott egység (pl. osztály) milyen céllal készült, milyen elemei vannak, és azokat mire lehet használni és mik az esetleges limitációk. Egy jó kódban kevés komment is elég ahhoz, hogy eligazodjunk benne, mert az osztályok, változók és metódusok nevei beszélő nevek, és egyértelmű, hogy mi a szerepük.
A Java részben pont azért nagyon népszerű, mert platformfüggetlen nyelv. A platformfüggetlenséget a JVM, azaz a Java Virtual Machine (JVM) biztosítja, amelyet viszont minden egyes platformra külön meg kell írni. A JRE a JVM működése szempontjából szükséges környezetet és egyéb eszközöket biztosít.
A JDK egy fejlesztői környezet, mely magában foglalja a JRE-t és ezáltal a JVM-et is.
Ha bővebben szeretnél erről és ehhez hasonló izgalmas tartalmakról tanulni, nézd végig ingyenes videókurzusunkat illetve tanfolyamainkat.
Szerző: Nagy Csongor