Hlavní navigace

Pohled pod kapotu JVM (10.část - instrukce pro práci s třídami a objekty)

14. 2. 2012
Doba čtení: 20 minut

Sdílet

V dnešní části seriálu o programovacím jazyce Java i o vlastnostech JVM se již popáté budeme zabývat popisem instrukčního souboru virtuálního stroje Javy. Zaměříme se na popis instrukcí, které jsou určené pro práci s třídami a objekty, zejména na instrukce pro přístup k atributům a metodám tříd i objektů.

Obsah

1. Instrukce sloužící pro přístup k atributům tříd (statickým datovým atributům)

2. Instrukce sloužící pro přístup k atributům objektů

3. Demonstrační příklad – přístup k atributům tříd a objektů

4. Instrukce invokestatic a invokevirtual

5. Demonstrační příklad – využití instrukcí invokestatic a invokevirtual v bajtkódu

6. Instrukce invokevirtual při volání metody deklarované v předkovi třídy

7. Instrukce invokespecial

8. Demonstrační příklady – využití instrukce invokespecial

9. Odkazy na Internetu

1. Instrukce sloužící pro přístup k atributům tříd (statickým datovým atributům)

Po popisu všech instrukcí sloužících pro provádění aritmetických a bitových operací i instrukcí umožňujících řízení běhu programu nám ještě zbývá si popsat několik dalších skupin instrukcí. Velmi důležité jsou především instrukce, které slouží pro práci s třídami a objekty. Tyto instrukce umožňují přístup k atributům tříd (tj. k atributům s nastaveným modifikátorem „static“, někdy se jim proto také říká statické datové atributy) i k atributům objektů. Další instrukce pak slouží pro volání statických i virtuálních metod, včetně volání konstruktorů. První skupinu instrukcí, se kterou se dnes seznámíme, tvoří instrukce sloužící pro přesuny dat mezi zásobníkem operandů a vybraným atributem třídy (statickým datovým atributem) či atributem objektu. Tyto instrukce nepracují přímo s adresou vybraného atributu (tu ostatně ani nelze nijak zjistit), ale s jeho signaturou, která je uložena v constant poolu. Díky tomu je umožněn dynamický výběr správného atributu prováděný až v čase běhu aplikace.

V následující tabulce jsou vypsány dvě instrukce, které umožňují práci s třídními (statickými) atributy:

# Instrukce Opkód Operandy Prováděná operace
1 getstatic 0×B2 highbyte, lowbyte získá z constant poolu záznam typu FieldReference a uloží hodnotu získaného atributu na zásobník operandů
2 putstatic 0×B3 highbyte, lowbyte opak předchozí instrukce: přesun hodnoty ze zásobníku operandů do statického atributu

Operandy highbyte a lowbyte tvoří šestnáctibitový index do constant poolu. Záznam na daném indexu musí být typu FieldReference, což je pro připomenutí záznam obsahující odkaz na další záznam typu Class (jméno třídy) a taktéž na záznam typu Name and Type (signatura metody či atributu).

Povšimněte si jedné zajímavosti: obě dvě instrukce vypsané v předchozí tabulce dokážou pracovat s jakýmkoli datovým typem atributu. Virtuální stroj Javy totiž ze signatury atributu zjistí jeho typ a překladač musí zajistit, aby se na zásobníku operandů skutečně nacházel operand daného typu (což platí pro instrukci putstatic, zde ovšem může dojít k implicitním typovým konverzím popsaným v další kapitole), jinak dojde k chybě při analýze bajtkódu při jeho načítání do virtuálního stroje Javy. Situace je poněkud složitější v případě polí, což je však problematika, kterou se budeme podrobněji zabývat až v navazující části tohoto seriálu (pro přístup k prvkům polí totiž existuje samostatná sada instrukcí rozdělených podle typu pole, resp. podle typů prvků uložených v poli).

U instrukce putstatic se kontroluje, zda atribut není finální (klíčové slovo final). Pokud tomu tak je, vyvolá se výjimka typu IllegalAccessE­rror. Samozřejmě se jedná o stav, který je kontrolován již při překladu, ovšem kvůli dynamičnosti celého JVM se může stát, že se v bajtkódu mění atribut třídy jiné verze, než byla verze použitá při překladu.

2. Instrukce sloužící pro přístup k atributům objektů

Instrukce getstatic a putstatic z předchozí kapitoly dokážou přečíst či naopak nastavit pouze statický atribut, tj. atribut společný pro všechny instance jedné třídy. Pro čtení či zápis atributu přiřazeného ke konkrétnímu objektu (=instanci třídy) se naproti tomu používají instrukce nazvané getfield a putfield, které se od obou předchozích instrukcí liší v jednom důležitém detailu: tyto instrukce očekávají, že na zásobníku operandů bude uložena reference na objekt daného typu.

V tomto ohledu je pro popis jednodušší instrukce getfield, která pracuje pouze s prvkem uloženým na vrcholu zásobníku operandů (TOS neboli top of stack). Tento prvek musí obsahovat referenci na objekt, jehož atribut se má přečíst. Reference je při provádění instrukce getfield ze zásobníku operandů odstraněna a namísto ní se na zásobník operandů uloží hodnota přečteného atributu.

Instrukce putfield pracuje s dvojicí prvků uložených na zásobníku operandů. Na TOS musí být uložena hodnota, na kterou se má atribut nastavit a pod TOS je očekávána reference na objekt daného typu. Oba prvky jsou po provedení instrukce putfield ze zásobníku operandů odstraněny. Navíc se při provádění instrukce putfield provádí stejná kontrola na „finálnost“ atributu, jako tomu bylo u instrukce putstatic.

Operační kódy a stručný popis instrukcí getfield a putfield jsou vypsány v následující tabulce:

# Instrukce Opkód Operandy Prováděná operace
3 getfield 0×B4 highbyte, lowbyte získá hodnotu atributu objektu, jehož reference je uložena na TOS a tuto hodnotu následně uloží na zásobník operandů
4 putfield 0×B5 highbyte, lowbyte obsah TOS se přenese do atributu objektu, jehož reference je uložena na druhém místě zásobníku operandů

Operandy highbyte a lowbyte opět společně tvoří šestnáctibitový index do constant poolu.

U instrukcí putstatic a putfield může docházet k typovým konverzím mezi prvkem uloženým na zásobníku operandů a skutečným typem atributu třídy či objektu. Ke konverzím dochází u atributů typu boolean, byte, char a short, protože hodnoty tohoto typu nemohou být na zásobníku operandů uloženy (některými důvody, proč tomu tak je, jsme se již zabývali v předchozích částech tohoto seriálu). Virtuální stroj Javy však dokáže automaticky a bez varování či dokonce zobrazení chybové zprávy převést hodnotu prvku typu int na hodnotu atributu typu boolean/byte/char/shor­t, a to i v těch případech, kdy při konverzi dojde k zanedbání nejvyšších bitů hodnoty.

3. Demonstrační příklad – přístup k atributům tříd a objektů

Způsob využití všech čtyř instrukcí popsaných v tabulkách uvedených v první kapitole a taktéž ve druhé kapitole si ukážeme na jednoduchém demonstračním příkladu. Ve zdrojovém kódu tohoto příkladu je deklarována čtveřice metod. Dvě metody jsou statické a v jejich těle se přistupuje k dvojici statických atributů, zatímco ve zbývajících dvou metodách se přistupuje k nestatickým atributům (atributům objektů). Zdrojový kód dnešního prvního demonstračního příkladu má tvar:

class Test1 {
 
    // staticky atribut
    static int staticField;
 
    // nestaticky atribut
    int nonStaticField;
 
    static void setStaticField(int x) {
        staticField = x;
    }
 
    static int getStaticField() {
        return staticField;
    }
 
    void setNonStaticField(int x) {
        this.nonStaticField = x;
    }
 
    int getNonStaticField() {
        return this.nonStaticField;
    }
 
}

V dalším popisu nám pomůže znalost obsahu constant poolu, především těch záznamů, které jsou ohraničeny hvězdičkou. Jedná se o záznamy typu FieldReference, z nichž lze vyčíst celou signaturu atributu (touto problematikou jsme se již taktéž v minulosti zabývali):

Velikost const. poolu: 25 prvku
  1   10  MethodRef         5  21    java/lang/Object.<init>()V
***************************************************************
  2    9  FieldRef          4  22    Test1.staticField:I
  3    9  FieldRef          4  23    Test1.nonStaticField:I
***************************************************************
  4    7  Class            24        Test1
  5    7  Class            25        java/lang/Object
  6    1  String                     "staticField"
  7    1  String                     "I"
  8    1  String                     "nonStaticField"
  9    1  String                     "<init>"
 10    1  String                     "()V"
 11    1  String                     "Code"
 12    1  String                     "LineNumberTable"
 13    1  String                     "setStaticField"
 14    1  String                     "(I)V"
 15    1  String                     "getStaticField"
 16    1  String                     "()I"
 17    1  String                     "setNonStaticField"
 18    1  String                     "getNonStaticField"
 19    1  String                     "SourceFile"
 20    1  String                     "Test1.java"
 21   12  Name and type    10   9    ()V  <init>
 22   12  Name and type     7   6    I  staticField
 23   12  Name and type     7   8    I  nonStaticField
 24    1  String                     "Test1"
 25    1  String                     "java/lang/Object"

Výpis bajtkódu vygenerovaného pro statickou metodu pojmenovanou getStaticField, v níž se čte hodnota statického atributu, která je následně použita jako návratová hodnota metody:

static int getStaticField();
  Code:
   0:   getstatic   #2; // uložení hodnoty atributu specifikovaného
                        // záznamem číslo 2 z constant poolu na zásobník
                        // operandů
   3:   ireturn         // výskok z metody s předáním návratové hodnoty
                        // uložené na TOS

Výpis bajtkódu vygenerovaného pro statickou metodu setStaticField, v níž se modifikuje hodnota statického atributu:

static void setStaticField(int);
  Code:
   0:   iload_0         // uložit na zásobník operandů
                        // první (a jediný) parametr metody
   1:   putstatic   #2; // přenos hodnoty uložené na zásobníku operandů
                        // do atributu specifikovaného záznamem číslo 2
                        // v constant poolu (Test1.staticField:I)
                        // Původní obsah TOS je odstraněn.
   4:   return          // výskok z metody bez předání návratové hodnoty

Výpis bajtkódu vygenerovaného pro nestatickou metodu getNonStaticFi­eld, v níž se čte hodnota nestatického atributu:

int getNonStaticField();
  Code:
   0:   aload_0         // uložit na zásobník operandů neviditelný
                        // parametr metody (this)
   1:   getfield    #3; // uložení hodnoty atributu specifikovaného
                        // záznamem číslo 3 z constant poolu na zásobník
                        // operandů. Původní obsah TOS (hodnota this)
                        // je použit a následně odstraněn.
   4:   ireturn         // výskok z metody s předáním návratové hodnoty
                        // uložené na TOS

Výpis bajtkódu vygenerovaného pro nestatickou metodu setNonStaticFi­eld, v níž se modifikuje hodnota nestatického atributu. Zde se pracuje s dvojicí prvků uložených na zásobníku operandů, jedná se tedy o nejsložitější případ:

void setNonStaticField(int);
  Code:
   0:   aload_0         // uložit na zásobník operandů neviditelný
                        // parametr metody (this)
   1:   iload_1         // uložit na zásobník operandů první viditelný
                        // parametr metody (int)
   2:   putfield    #3; // přenos hodnoty uložené na zásobníku operandů
                        // do atributu specifikovaného záznamem číslo 3
                        // v constant poolu (Test1.nonStaticField:I)
                        // Původní obsah TOS i hodnoty pod TOS jsou odstraněny
   5:   return          // výskok z metody bez předání návratové hodnoty

4. Instrukce invokestatic a invokevirtual

Další skupina instrukcí slouží pro volání metod, ať již se jedná o metody statické, nestatické (většinou tedy o metody virtuální), tak i o metody speciální, mezi něž patří zejména konstruktory. Základními instrukcemi patřícími do této skupiny jsou instrukce nazvané invokestatic a invokevirtual. Instrukce invokestatic slouží, jak již ostatně název této instrukce napovídá, k zavolání statické metody, tj. metody, kterou je možné zavolat i v případě, že neexistuje žádná instance třídy, v níž je tato metoda deklarována (samozřejmě za předpokladu, že jsou vhodně nastavena přístupová práva k metodě). Naproti tomu druhá instrukce ze stejné skupiny – instrukce invokevirtual – je určena pro zavolání nestatické metody, tj. metody, které je jako první (skrytý) parametr předána reference na objekt, v jehož rámci je metoda zavolána (metoda však nesmí být privátní, v tomto případě by se musela použít instrukce invokespecial). Obě instrukce pracují se signaturami metod uloženými v constant poolu (je to trošku podobné instrukcím uvedeným v první a ve druhé kapitole) a před zavoláním statické či nestatické metody je nutné na zásobník operandů uložit všechny parametry, které se mají volané metodě předat.

Formát obou zmíněných instrukcí je vypsán v tabulce zobrazené pod tímto odstavcem:

# Instrukce Opkód Operandy Prováděná operace
1 invokestatic 0×B8 highbyte, lowbyte zavolání statické metody s předáním parametrů této metodě
2 invokevirtual 0×B6 highbyte, lowbyte zavolání nestatické metody s předáním hodnoty this a všech dalších parametrů

Operandy highbyte a lowbyte tvoří šestnáctibitový index do constant poolu. Záznam uložený na daném indexu musí být typu MethodReference, což je pro připomenutí záznam obsahující odkaz na záznam typu Class (jméno třídy) a taktéž na záznam typu Name and Type (signatura metody či atributu). Až na rozdíl mezi FieldReference a MethodReference je tedy význam operandů highbyte a lowbyte stejný, jako tomu bylo u instrukcí putfield, putstatic, getfield a getstatic.

V případě instrukce invokevirtual je však situace poněkud složitější, protože virtuální stroj Javy musí zjistit, metoda které třídy má být ve skutečnosti zavolána – může se totiž jednat o metodu předka, a to v libovolné úrovni (klidně se může jednat i o metodu prapředka všech tříd – třídy Object). Proto virtuální stroj Javy rekurzivně prochází předky třídy, jejíž instance je uložena na zásobníku operandů a hledá metodu se signaturou stejnou, jako je signatura uložená v constant poolu. Jakmile je taková metoda nalezena, je rekurzivní vyhledávání je ukončeno a nalezená metoda je zavolána se všemi parametry, včetně skrytého parametru představujícího this (referenci na objekt, pro nějž je metoda zavolána).

5. Demonstrační příklad – využití instrukcí invokestatic a invokevirtual v bajtkódu

Způsob využití instrukcí invokestatic a invokevirtual si ukážeme na demonstračním příkladu se čtveřicí metod. První dvě metody, z nichž jedna je statická (staticMethod) a druhá nestatická (nonStaticMet­hod), mají dva parametry typu int, ale pro jednoduchost je jejich tělo prázdné. Tyto metody jsou volány z dalších dvou metod, z nichž je opět jedna metoda statická (callStaticMet­hod) a druhá nestatická (callNonStatic­Method):

class Test2 {
 
    static void staticMethod(int x, int y) {
        // nic = prazdne telo
    }
 
    void nonStaticMethod(int x, int y) {
        // nic = prazdne telo
    }
 
    void callStaticMethod() {
        staticMethod(10, 20);
    }
 
    void callNonStaticMethod() {
        nonStaticMethod(10, 20);
    }
}

Podobně jako u prvního demonstračního příkladu, i zde si nejdříve ukážeme obsah constant poolu se zvýrazněním záznamů, s nimiž se bude dále pracovat:

Velikost const. poolu: 21 prvku
  1   10  MethodRef         5  17    java/lang/Object.<init>()V
***************************************************************
  2   10  MethodRef         4  18    Test2.staticMethod(II)V
  3   10  MethodRef         4  19    Test2.nonStaticMethod(II)V
***************************************************************
  4    7  Class            20        Test2
  5    7  Class            21        java/lang/Object
  6    1  String                     "<init>"
  7    1  String                     "()V"
  8    1  String                     "Code"
  9    1  String                     "LineNumberTable"
 10    1  String                     "staticMethod"
 11    1  String                     "(II)V"
 12    1  String                     "nonStaticMethod"
 13    1  String                     "callStaticMethod"
 14    1  String                     "callNonStaticMethod"
 15    1  String                     "SourceFile"
 16    1  String                     "Test2.java"
 17   12  Name and type     7   6    ()V  <init>
 18   12  Name and type    11  10    (II)V  staticMethod
 19   12  Name and type    11  12    (II)V  nonStaticMethod
 20    1  String                     "Test2"
 21    1  String                     "java/lang/Object"

Bajtkód volané statické metody nazvané staticMethod je velmi jednoduchý:

static void staticMethod(int, int);
  Code:
                          // tato metoda nemá žádné tělo, obsahuje jen
                          // instrukci pro návrat
   0:   return            // výskok z metody bez předání návratové hodnoty

Podobně je tomu u bajtkódu nestatické metody nonStaticMethod:

void nonStaticMethod(int, int);
  Code:
                          // tato metoda nemá žádné tělo, obsahuje jen
                          // instrukci pro návrat
   0:   return            // výskok z metody bez předání návratové hodnoty

Při volání statické metody se na zásobník operandů ukládají pouze viditelné parametry volané metody:

void callStaticMethod();
  Code:
   0:   bipush  10        // první parametr volané statické metody
   2:   bipush  20        // druhý parametr volané statické metody
   4:   invokestatic #2;  // Method staticMethod:(II)V
                          // oba prvky na zásobníku operandů se při
                          // návratu odstraní
   7:   return            // výskok z metody bez předání návratové hodnoty

Složitější je to v případě metody nestatické, protože na zásobník operandů je nutné kromě viditelných parametrů uložit i neviditelný parametr this:

void callNonStaticMethod();
  Code:
   0:   aload_0           // implicitní parametr (this) volané nestatické metody
   1:   bipush  10        // první viditelný parametr volané nestatické metody
   3:   bipush  20        // druhý viditelný parametr volané nestatické metody
   5:   invokevirtual #3; // Method nonStaticMethod:(II)V
                          // všechny tři prvky na zásobníku operandů se při
                          // návratu odstraní
   8:   return            // výskok z metody bez předání návratové hodnoty

6. Instrukce invokevirtual při volání metody deklarované v předkovi třídy

Ve čtvrté kapitole jsme si řekli, že způsob zjištění metody, která se má skutečně zavolat instrukcí invokevirtual je poměrně složitý, protože daná metoda může být deklarována v některém předkovi třídy, nikoli v samotné třídě (přesněji řečeno instanci této třídy), pro kterou se metoda volá. Toto chování si můžeme ukázat na demonstračním příkladu s trojicí tříd ClassA, ClassB a ClassC tvořících hierarchii, na jejímž vrcholu stojí třída ClassA (jejímž předkem je samozřejmě třída Object):

class ClassA {
    void methodA() {
    }
}
 
class ClassB extends ClassA {
}
 
class ClassC extends ClassB {
    void methodC() {
        // zde se vola metoda deklarovana
        // v prapredkovi = tride ClassA
        methodA();
    }
}

Podívejme se nyní, jak vypadá bajtkód metody methodC:

void methodC();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokevirtual   #2; // Method methodA:()V
                            // ClassC.methodA:()V
   4:   return

Ve skutečnosti se však v bajtkódu třídy ClassC žádná metoda nazvaná methodA nenachází. Z tohoto důvodu musí virtuální stroj Javy danou metodu postupně hledat v předcích dané třídy, tj. nejdříve ve třídě ClassB (bez úspěchu) a posléze ve třídě ClassA (zde již s úspěchem).

7. Instrukce invokespecial

Další instrukcí určenou pro volání metod, je instrukce nazvaná invokespecial. Tato instrukce se používá zejména při inicializaci objektu, konkrétně při volání konstruktoru předka poté, co je objekt vytvořen, ale taktéž při volání privátních metod, popř. překrytých metod. Struktura instrukce invokespecial je vypsána v následující tabulce:

# Instrukce Opkód Operandy Prováděná operace
1 invokespecial 0×B7

8. Demonstrační příklady – využití instrukce invokespecial

Jak jsme si již řekli v předchozí kapitole, má instrukce invokespecial hned několik využití. Prvním z nich je volání privátní metody. Rozdíl mezi voláním privátní a neprivátní metody ilustruje následující demonstrační příklad:

class PrivateAndPublicCall1 {
 
    private void privateMethod() {
        // nic = prazdne telo
    }
 
    public void publicMethod() {
        // nic = prazdne telo
    }
 
    void call() {
        privateMethod();
        publicMethod();
    }
}

Zajímat nás samozřejmě bude bajtkód metody call, která volá jak privátní metodu, tak i metodu veřejnou:

void call();
  Code:
   0:   aload_0             // uložit referenci "this"
                            // na zásobník operandů
   1:   invokespecial   #2; // Method privateMethod:()V
                            // privátní metoda = invokespecial
   4:   aload_0             // uložit referenci "this"
                            // na zásobník operandů
   5:   invokevirtual   #3; //Method publicMethod:()V
                            // veřejná metoda = invokevirtual
   8:   return

Aby to však nebylo tak jednoduché, je situace při volání statických privátních a veřejných metod odlišná, protože se zde použije stejná instrukce:

class PrivateAndPublicCall2 {
 
    private static void privateStaticMethod() {
        // nic = prazdne telo
    }
 
    public static void publicStaticMethod() {
        // nic = prazdne telo
    }
 
    void callStatic() {
        privateStaticMethod();
        publicStaticMethod();
    }
 
}

V tomto případě je bajtkód metody callStatic následující:

void callStatic();
  Code:
   0:   invokestatic    #4; //Method privateStaticMethod:()V
   3:   invokestatic    #5; //Method publicStaticMethod:()V
   6:   return

Instrukce invokespecial se využívá taktéž při inicializaci objektů, což si ukážeme na poněkud odlišném demonstračním příkladu s následujícím zdrojovým kódem, v jehož metodách se konstruují instance tříd Test3, Integer a String:

class Test3 {
 
    public static void testInvokeSpecial1() {
        new Test3();
    }
 
    public static Integer testInvokeSpecial2() {
        return new Integer(42);
    }
 
    public static void testInvokeSpecial3() {
        new String("pokus");
    }
 
}

Opět nebude na škodu si ukázat obsah constant poolu uloženého v bajtkódu. Důležité záznamy jsou ohraničeny hvězdičkami:

Velikost const. poolu: 29 prvku
  1   10  MethodRef         9  20    java/lang/Object.<init>()V
  2    7  Class            21        Test3
***************************************************************
  3   10  MethodRef         2  20    Test3.<init>()V
***************************************************************
  4    7  Class            22        java/lang/Integer
***************************************************************
  5   10  MethodRef         4  23    java/lang/Integer.<init>(I)V
***************************************************************
  6    7  Class            24        java/lang/String
  7    8  String const     25        "pokus"
***************************************************************
  8   10  MethodRef         6  26    java/lang/String.<init>(Ljava/lang/String;)V
***************************************************************
  9    7  Class            27        java/lang/Object
 10    1  String                     "<init>"
 11    1  String                     "()V"
 12    1  String                     "Code"
 13    1  String                     "LineNumberTable"
 14    1  String                     "testInvokeSpecial1"
 15    1  String                     "testInvokeSpecial2"
 16    1  String                     "()Ljava/lang/Integer;"
 17    1  String                     "testInvokeSpecial3"
 18    1  String                     "SourceFile"
 19    1  String                     "Test3.java"
 20   12  Name and type    11  10    ()V  <init>
 21    1  String                     "Test3"
 22    1  String                     "java/lang/Integer"
 23   12  Name and type    28  10    (I)V  <init>
 24    1  String                     "java/lang/String"
 25    1  String                     "pokus"
 26   12  Name and type    29  10    (Ljava/lang/String;)V  <init>
 27    1  String                     "java/lang/Object"
 28    1  String                     "(I)V"
 29    1  String                     "(Ljava/lang/String;)V"

V bajtkódu metody testInvokeSpe­cial1 se nejdříve pomocí instrukce new vytvoří instance třídy Test3 a posléze se zavolá speciální metoda nazvaná <init>, která (poněkud zjednodušeně řečeno) odpovídá konstruktoru této třídy. Povšimněte si také dvojice instrukcí dup a pop na adresách 3 a 7. Tyto instrukce slouží pro úschovu a následné odstranění reference na objekt vytvořený pomocí new, i když ve skutečnosti není tato reference nikde použita (instrukce invokespecial taktéž využívá referenci na vytvořený objekt, ovšem tato reference je při zpracování této instrukce z TOS odstraněna):

CS24_early

public static void testInvokeSpecial1();
  Code:
   0:   new #2;           // vytvoření instance třídy Test3
   3:   dup               // invokespecial využije a zahodí referenci
                          // uloženou na TOS, proto javac vygeneruje
                          // tuto instrukci, která referenci na
                          // zásobníku zachová
   4:   invokespecial #3; // zavolání konstruktoru bez parametrů:
                          // Method "<init>":()V
                          // na zásobníku operandů je uložena reference
                          // vytvořeného objektu
   7:   pop               // objekt je sice vytvořen, ale jeho referenci
                          // nevyužíváme: je odstraněna pomocí pop
   8:   return            // výskok z metody bez předání návratové hodnoty

V metodě testInvokeSpe­cial2 je vytvořen objekt typu Integer, přičemž je využit jednoparametrický konstruktor této třídy. Povšimněte si, že v bajtkódu je opět využita instrukce dup pro vytvoření kopie reference na vytvořený objekt, a v tomto případě je kopie skutečně využita instrukcí areturn:

public static java.lang.Integer testInvokeSpecial2();
  Code:
   0:   new #4;           // vytvoření instance třídy java.lang.Integer
   3:   dup               // invokespecial využije a zahodí referenci
                          // uloženou na TOS, proto javac vygeneruje
                          // tuto instrukci, která referenci na
                          // zásobníku zachová
   4:   bipush  42        // parametr předaný konstruktoru třídy Integer
   6:   invokespecial #5; // zavolání konstruktoru s jedním parametrem:
                          // Method java/lang/Integer."<init>":(I)V
                          // na zásobníku operandů je uložena reference
                          // vytvořeného objektu
   9:   areturn           // výskok z metody s předáním návratové hodnoty

V metodě testInvokeSpe­cial3 je nejprve vytvořena instance třídy String, a to mimochodem značně neefektivním a amatérským způsobem :-) Konstruktor třídy String je volán s jedním parametrem, kterým je řetězcová konstanta uložená v constant poolu. I přesto, že se s referencí na vytvořený objekt nijak nepracuje, vygeneruje překladač dvojici instrukcí dup a pop, podobně jako tomu bylo v metodě testInvokeSpe­cial1:

public static void testInvokeSpecial3();
  Code:
   0:   new #6;           // vytvoření instance třídy java.lang.String
   3:   dup               // invokespecial využije a zahodí referenci
                          // uloženou na TOS, proto javac vygeneruje
                          // tuto instrukci, která referenci na
                          // zásobníku zachová
   4:   ldc #7;           // String pokus
   6:   invokespecial #8; // zavolání konstruktoru s jedním parametrem:
                          // Method java/lang/String."<init>":(Ljava/lang/String;)V
   9:   pop               // objekt je sice vytvořen, ale jeho referenci
                          // nevyužíváme: je odstraněna pomocí pop
   10:  return            // výskok z metody bez předání návratové hodnoty

9. Odkazy na Internetu

  1. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mo­bilefish.com/tu­torials/java/ja­va_quickguide_jvm_in­struction_set­.html
  2. The JVM Instruction Set
    http://mpdebo­er.home.xs4all­.nl/scriptie/no­de14.html
  3. Control Flow in the Java Virtual Machine
    http://www.ar­tima.com/under­thehood/flowP­.html
  4. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.ro­ot.cz/clanky/vy­uziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  5. Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
    http://www.ro­ot.cz/clanky/jam­vm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/
  6. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun­.com/docs/book­s/jvms/second_e­dition/html/VMSp­ecTOC.doc.html
  7. The class File Format
    http://java.sun­.com/docs/book­s/jvms/second_e­dition/html/Clas­sFile.doc.html
  8. javap – The Java Class File Disassembler
    http://docs.o­racle.com/java­se/1.4.2/docs/to­oldocs/window­s/javap.html
  9. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.di­e.net/man/1/j­avap-java-1.6.0-openjdk
  10. Using javap
    http://www.ide­velopment.info/da­ta/Programmin­g/java/miscella­neous_java/Usin­g_javap.html
  11. Examine class files with the javap command
    http://www.techre­public.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  12. BCEL Home page
    http://common­s.apache.org/bcel/
  13. BCEL Manual
    http://common­s.apache.org/bcel/ma­nual.html
  14. Byte Code Engineering Library (Wikipedia)
    http://en.wiki­pedia.org/wiki/BCEL
  15. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ib­m.com/developer­works/java/li­brary/j-dyn0414/
  16. Bytecode Engineering
    http://book.chi­naunix.net/spe­cial/ebook/Co­re_Java2_Volu­me2AF/0131118269/ch13lev­1sec6.html
  17. BCEL Tutorial
    http://www.smfsup­port.com/suppor­t/java/bcel-tutorial!/
  18. ASM Home page
    http://asm.ow2­.org/
  19. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2­.org/users.html
  20. ObjectWeb ASM (Wikipedia)
    http://en.wiki­pedia.org/wiki/Ob­jectWeb_ASM
  21. Java Bytecode BCEL vs ASM
    http://james.o­negoodcookie.com/2005/10­/26/java-bytecode-bcel-vs-asm/
  22. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2­.org/eclipse/in­dex.html
  23. aspectj (Eclipse)
    http://www.eclip­se.org/aspectj/
  24. Aspect-oriented programming (Wikipedia)
    http://en.wiki­pedia.org/wiki/As­pect_oriented_pro­gramming
  25. AspectJ (Wikipedia)
    http://en.wiki­pedia.org/wiki/As­pectJ
  26. EMMA: a free Java code coverage tool
    http://emma.sou­rceforge.net/
  27. Cobertura
    http://cobertu­ra.sourceforge­.net/
  28. FindBugs
    http://findbug­s.sourceforge­.net/
  29. GNU Classpath
    www.gnu.org/s/clas­spath/
  30. Java VMs Compared
    http://bugblog­ger.com/java-vms-compared-160/
  31. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp­.org/en/jsr/de­tail?id=223
  32. Scripting for the Java Platform
    http://java.sun­.com/developer/techni­calArticles/J2SE/Des­ktop/scriptin­g/
  33. Scripting for the Java Platform (Wikipedia)
    http://en.wiki­pedia.org/wiki/Scrip­ting_for_the_Ja­va_Platform
  34. Java Community Process
    http://en.wiki­pedia.org/wiki/Ja­va_Specificati­on_Request
  35. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  36. Great Computer Language Shootout
    http://c2.com/cgi/wi­ki?GreatCompu­terLanguageSho­otout
  37. Java performance
    http://en.wiki­pedia.org/wiki/Ja­va_performance
  38. Trying the prototype
    http://mail.o­penjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  39. Better closures (for Java)
    http://blogs.sun­.com/jrose/en­try/better_clo­sures
  40. Lambdas in Java: An In-Depth Analysis
    http://www.in­foq.com/articles/lam­bdas-java-analysis
  41. Class ReflectiveOpe­rationExcepti­on
    http://downlo­ad.java.net/jdk7/doc­s/api/java/lan­g/ReflectiveO­perationExcep­tion.html
  42. Proposal: Indexing access syntax for Lists and Maps
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  43. Proposal: Elvis and Other Null-Safe Operators
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  44. Java 7 : Oracle pushes a first version of closures
    http://www.bap­tiste-wicht.com/2010/05­/oracle-pushes-a-first-version-of-closures/
  45. Groovy: An agile dynamic language for the Java Platform
    http://groovy­.codehaus.org/O­perators
  46. Better Strategies for Null Handling in Java
    http://www.sli­deshare.net/Step­han.Schmidt/bet­ter-strategies-for-null-handling-in-java
  47. Control Flow in the Java Virtual Machine
    http://www.ar­tima.com/under­thehood/flowP­.html
  48. Java Virtual Machine
    http://en.wiki­pedia.org/wiki/Ja­va_virtual_machi­ne
  49. ==, .equals(), compareTo(), and compare()
    http://leepoin­t.net/notes-java/data/expres­sions/22compa­reobjects.html
  50. New JDK7 features
    http://openjdk­.java.net/pro­jects/jdk7/fe­atures/
  51. Project Coin: Bringing it to a Close(able)
    http://blogs.sun­.com/darcy/en­try/project_co­in_bring_close
  52. CloseableFinder source code
    http://blogs.sun­.com/darcy/re­source/Projec­tCoin/Closeable­Finder.java
  53. Joe Darcy blog about JDK
    http://blogs.sun­.com/darcy
  54. Java 7 – more dynamics
    http://www.bap­tiste-wicht.com/2010/04­/java-7-more-dynamics/
  55. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun­.com/developer/techni­calArticles/Dyn­TypeLang/index­.html

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.