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és

A 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és

A 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.

Tekintsü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és

A 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:

Végrehajtás

szerkesztés

A 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és

A 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és
  1. VM Spec - Reserved Opcodes
  2. Understanding bytecode makes you a better programmer
  3. A Formal Introduction to the Compilation of Java, Stephan Diehl, "Software - Practice and Experience", Vol. 28(3), pages 297-327, March 1998.
  4. A számok itt hexadecimális formában állnak, ld. A hexadecimális szám felírási módjai
  5. Jasmin Home Page. [2020. április 17-i dátummal az eredetiből archiválva]. (Hozzáférés: 2020. június 15.)
  6. 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.)
  7. Nutter, Charles: InvokeDynamic: Actually Useful?, 2007. január 3. (Hozzáférés: 2008. január 25.)
  8. see JSR 292

Fordítás

szerkesztés

Ez 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.

További információk

szerkesztés
Az angol Wikikönyvekben
további információk találhatók
Java bájtkód témában.