Java bájtkód
A Java bájtkód a Java virtuális gép (JVM) által végrehajtható utasítások megjelenési formája; tehát olyan bájtsorozat, amely a JVM által végrehajtható utasításokat reprezentál. Felfogható a virtuális gép „gépi kódjaként” is, bár ez a kód nem egy adott hardveres architektúrához kötődik, hanem a hardver fölötti rétegen működő virtuális gép specifikációjához. A Java bájtkódot a JVM értelmezője hajtja végre, az emulátorokhoz hasonlóan, vagy futási időben fordul le a gazdagép hardverének gépi kódjára (JIT módszer).
Minden bájtkód egy egybájtos – egy bájton tárolt – opkód, bár néhány opkódnak paraméterei is vannak, amelyek így több-bájtos utasításokat eredményeznek. A bájtkódok nem használják ki az egy bájton rendelkezésre álló 256 lehetőséget. 51 kód fenntartott a jövőbeli felhasználásra. Ezen felül a Sun Microsystems, a Java platform eredeti kifejlesztője, három értéket véglegesen „nem megvalósítottnak” jelölt, tehát ezekhez a kódokhoz a jövőben sem lesz művelet hozzárendelve.[1]
Kapcsolat a Java nyelvvel
szerkesztésA Java programozónak nem kell tisztában lennie vagy foglalkoznia a java bájtkóddal, mivel az automatikusan készül a program fordításakor. Egyes fejlesztői csoportok véleménye szerint a java bájtkód lényegének – pl. a Java fordítóprogram hogyan készíti el a kódot vagy a virtuális gép hogyan hajtja végre a kódot – akár csak nagy vonalakban való ismerete segítheti a programozót, ahhoz hasonlóan, ahogy az assembly ismerete segíti a C vagy C++ programozókat.[2][3]
Utasítások
szerkesztés- Lásd még: Java bájtkód utasítások listája
Az opkódok a virtuális gép elemi utasításainak kódjai, ill. minden utasítást egy egybájtos opkód jelöl. Mivel egy bájt 256 lehetséges értéket vehet fel, ezért a lehetséges opkódok száma is ugyanennyi. A 256 érték közül a 0x00[4] értéktől a 0xca értékig terjedő szakaszba eső, valamint a 0xfe és a 0xff értékekhez van hozzárendelve utasítás. A 0xca egy a hibakereső programok / debuggerek számára fenntartott utasítás, maga a nyelv nem használja. Hasonlóan, a 0xfe és 0xff kódokat sem használja a nyelv, ezek a virtuális gép belső használatára vannak fenntartva.
Az utasítások nagy vonalakban a következő csoportokba sorolhatók:
- Betöltés és tárolás (pl. aload_0, istore)
- Számolási (aritmetikai) és logikai utasítások (pl. ladd, fcmpl)
- Típuskonverzió (pl. i2b, d2i)
- Objektumok létrehozása és objektumműveletek (new, putfield)
- Operandusverem kezelése (pl. swap, dup2)
- Programvezérlési utasítások (pl. ifeq, goto)
- Metódushívási és visszatérési utasítások (pl. invokespecial, areturn)
Ezeken felül van néhány utasítás olyan specializáltabb feladatok céljaira is, mint pl. a kivételek kiváltása, szinkronizáció, egyebek.
A legtöbb utasításnak van valamilyen előtagja (prefixe) és utótagokat (szuffixumok) is kaphat, amelyek az utasítás által kezelt operandusok típusára vonatkoznak. Ezek a következők:
Prefix/Suffix | Operandus típus |
---|---|
i |
integer |
l |
long |
s |
short |
b |
byte |
c |
character |
f |
float |
d |
double |
z |
boolean |
a |
reference |
Például az „iadd” két integer érték, míg a „dadd” két duplapontos érték összeadását jelöli. A „const”, „load”, és „store” utasítások „_n” alakú utótagokat kaphatnak, ahol az n egy 0 és 3 közötti szám lehet a „load” és „store” utasításoknál. A „const” után az n maximuma változik a típustól függően. A „const” utasítások a megadott típusú értékeket a verembe teszik (a verem tetejére helyezik, push művelet). Például az „iconst_5” egy integer 5 értéket tesz a verembe míg a „dconst_1” egy duplapontos 1 értéket. Van egy „aconst_null” utasítás is, amely a „null” értéket teszi a verembe (ezt az értéket a Java nyelvben általánosan használják). A „load” és a „store” utasításokban az n értéke a változó helyfoglalására vonatkozik a változólistában. Az „aload_0” utasítás a 0. változóban tárolt objektumot helyezi a verem tetejére (ami általában a „this” objektum). Az „istore_1” a verem tetején lévő integer értéket az 1. változóba írja. A nagyobb számú / indexű változók esetén az utótag elmarad és az utasításban operátorokkal jelölik azokat.
Számítási modell
szerkesztésA Java bájtkód számítási modellje megegyezik a veremalapú programozási nyelvek számítási modelljével. Például lássuk egy x86-os processzor assembly nyelvű kódrészletét:
mov eax, byte [ebp-4]
mov edx, byte [ebp-8]
add eax, edx
mov ecx, eax
Ez a kód két értéket ad össze és az eredményt egy harmadik helyre (itt: regiszterbe) írja. Az ugyanezt végrehajtó visszafejtett bájtkód a következő lehet:
0 iload_1
1 iload_2
2 iadd
3 istore_3
Ebben az esetben a két összeadandó érték a verembe kerül, ahonnan az összeadó utasítás előveszi azokat, összeadja az értékeket, és az eredményt a verembe teszi. A tároló utasítás a verem tetején lévő értéket kiveszi és a változóba (a változótáblázatban megjelölt területre) írja. Az utasítások előtti számok csak az utasítások indexét (ofszetjét) mutatják az adott metódus kezdetétől számítva. A veremorientált modell kiterjed a nyelv objektumorientált szempontjaira is. Például egy "getName()" nevű metódus hívása a következőképpen nézhet ki:
Method java.lang.String getName()
0 aload_0 // A "this" objektum a változótáblázat 0. helyén van tárolva
1 getfield #5 <Field java.lang.String name>
// Ez az utasítás levesz (pop) egy objektumot a stack tetejéről, előveszi abból
// a megadott mező értékét, majd a mező értéket a verembe rakja.
// Ebben a példában a "name" mező megfelel az ötödik konstansnak
// a class állandó-területén (constant pool).
4 areturn // Visszaadja a verem tetején álló objektumot a metódusból.
Példa
szerkesztésTekintsük az alábbi Java kódot:
outer:
for (int i = 2; i < 1000; i++) {
for (int j = 2; j < i; j++) {
if (i % j == 0)
continue outer;
}
System.out.println (i);
}
A Java fordító a következő Java bájtkódot állíthatja elő a fordítás során, feltételezve, hogy a fenti kódrészlet egy metódus része:
0: iconst_2
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 44
9: iconst_2
10: istore_2
11: iload_2
12: iload_1
13: if_icmpge 31
16: iload_1
17: iload_2
18: irem
19: ifne 25
22: goto 38
25: iinc 2, 1
28: goto 11
31: getstatic #84; //Field java/lang/System.out:Ljava/io/PrintStream;
34: iload_1
35: invokevirtual #85; //Method java/io/PrintStream.println:(I)V
38: iinc 1, 1
41: goto 2
44: return
Előállítás
szerkesztésA legismertebb nyelv, amely bájtkódot produkál a JVM számára, a Java. Eredetileg csak egy fordító létezett, a Sun Microsystems javac fordítóprogramja, amely a Java nyelvű forráskódot Java bájtkódra fordítja. de mivel a Java bájtkódra vonatkozó specifikációk mára nyilvánosak és rendelkezésre állnak, más szereplők is készítettek Java bájtkódot előállító fordítóprogramokat. Néhány példa:
- Jikes, Java forráskódból Java bájtkódot készít (IBM fejlesztés, C++ nyelven készült, Java 5-ig kompatibilis, részben)
- Espresso, Java forráskódból Java bájtkódot készít (csak Java 1.0)
- GCJ, „GNU Compiler for Java”, Java forráskódból Java bájtkódot készít; képes natív gépi kódot is készíteni, a GNU Compiler Collection (GCC) része.
Néhány projektben Java assembler is található, amely lehetővé teszi a Java bájtkód kézi elkészítését. Az assemly kód géppel is generálható, például a fordítóprogram célplatform megjelölésével. Néhány Java assembler:
- Jasmin, Java osztályok leírhatók egyszerű assembly-szerű szintaxissal és JVM utasításkészlettel, Java class fájlt készít[5]
- Jamaica, egy macro assembly nyelv a Java virtuális géphez. Java szintaxis használható az osztály interfész leírásában. A metódusok törzse Java bájtkód utasításokkal írható.[6]
További fordítóprogramok is készültek, amelyek különböző programnyelvekről képesek Java bájtkód előállítására, a fordítóprogram célplatformjaként a Java virtuális gép megjelölésével:
- ColdFusion
- JRuby és Jython, Ruby- és Python-alapú szkriptnyelvek
- Groovy, Java nyelven alapuló szkriptnyelv
- Scala, egy típusbiztos általános célú programozási nyelv, amely támogatja az objektumorientált és funkcionális programozást
- JGNAT és AppletMagic, Ada nyelvű forráskódból fordít Java bájtkódra
- C-ről Java bájtkódra fordító fordítóprogramok
- Clojure
- MIDletPascal
- JavaFX Script kód szintén lefordítható Java bájtkódra
Végrehajtás
szerkesztésA Java bájtkódot a Java virtuális gép általi végrehajtásra tervezték. Mostanára több Java virtuális gép is elkészült, különböző platformokra, egyesek nyíltak, mások kereskedelmi célú termékek.
Ha valamilyen okból a Java bájtkód végrehajtása a Java virtuális géppel nemkívánatos, akkor a fejlesztőnek lehetősége van a Java forrásprogramot vagy a Java bájtkódot natív gépi kódra fordítani; ezt megteheti ingyenes eszközökkel is, pl. GCJ.
Néhány processzor képes a Java bájtkód natív végrehajtására; ezeket a processzorokat nevezik Java processzoroknak.
Dinamikus nyelvek támogatása
szerkesztésA Java virtuális gép nyújt némi támogatást a dinamikus típusú nyelvekhez. A JVM utasítások többsége statikus típusú – abban az értelemben, hogy a metódushívások paramétereinek (signature) típusellenőrzése fordítási időben történik, és nincs olyan mechanizmus, amely elhalasztaná / kitolná ezt a döntést a futási időbe, vagy valamilyen alternatív módszerrel kezelné a metódusok indítását.[7]
A JSR 292 (Supporting Dynamically Typed Languages on the Java™ Platform, Dinamikus típusú nyelvek támogatása a Java™ Platformon)[8] specifikációs javaslat bevezet egy új invokedynamic
utasítást a JVM szintjén, amely lehetővé teszi a dinamikus típusellenőrzésen alapuló metódushívást (amely a már létező, statikus típusellenőrzésű invokevirtual
utasítás helyett lenne használható). A Da Vinci Machine egy virtuális gép prototípus-szintű megvalósítása, amely a dinamikus nyelvek támogatását célzó JVM kiterjesztéseket gyűjti egybe. A J2SE 7-es változatát támogató virtuális gépek szintén tartalmazzák az invokedynamic
opkódot.
Kapcsolódó szócikkek
szerkesztésJegyzetek
szerkesztés- ↑ VM Spec - Reserved Opcodes
- ↑ Understanding bytecode makes you a better programmer
- ↑ A Formal Introduction to the Compilation of Java, Stephan Diehl, "Software - Practice and Experience", Vol. 28(3), pages 297-327, March 1998.
- ↑ A számok itt hexadecimális formában állnak, ld. A hexadecimális szám felírási módjai
- ↑ Jasmin Home Page. [2020. április 17-i dátummal az eredetiből archiválva]. (Hozzáférés: 2020. június 15.)
- ↑ Jamaica: The Java Virtual Machine (JVM) Macro Assembler. [2012. június 24-i dátummal az eredetiből archiválva]. (Hozzáférés: 2013. június 30.)
- ↑ Nutter, Charles: InvokeDynamic: Actually Useful?, 2007. január 3. (Hozzáférés: 2008. január 25.)
- ↑ see JSR 292
Fordítás
szerkesztésEz a szócikk részben vagy egészben a Java bytecode című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.
Források
szerkesztés- Több szerző.szerk.: Nyékyné Gaizler Judit, Lakatos Attila: Q. Fejezet / JVM: Java Virtuális Gép, Java 2 útikalauz programozóknak: 5.0 (PDF) (magyar nyelven), Budapest: ELTE Természettudományi Kar [1996] (2008). ISBN 9789630640923. Hozzáférés ideje: 2013.
További információk
szerkesztés- Sun's Java Virtual Machine Specification
- Programming Languages for the Java Virtual Machine
- Bytecode Visualizer – bájtkód nézegető és debugger (ingyenes Eclipse plugin)
- AdaptJ StackTrace – bájtkód szintű debugger eszköz, verem, változók és végrehajtás vezérléssel
- Java Class Unpacker – Total Commander plugin, class fájlok megnyitása, nézegető; metódusok, mezők és bájtkód megjelenítés