Pohled pod kapotu JVM (5.část - popis virtuálního stroje Javy)

Pavel Tišnovský 10. 1. 2012

V dnešní části seriálu o programovacím jazyce Java se budeme zabývat popisem vlastního virtuálního stroje Javy, v němž jsou zpracovávány instrukce bajtkódu tvořící těla jednotlivých metod. Popíšeme si strukturu virtuálního stroje v době běhu aplikace a nezapomeneme ani na základní informace o instrukčním souboru.

Obsah

1. Virtuální stroj Javy – JVM

2. Interprety bajtkódu versus just-in-time překladače

3. Programátorský pohled na virtuální stroj Javy

4. Sdílené paměťové prostory: halda (heap), method area a runtime constant pool

5. Paměťový prostor přidělený každému vláknu: zásobník, zásobníkové rámce (stack frames)

6. Datové typy zpracovávané virtuálním strojem Javy

7. Zásobník operandů

8. Využití zásobníku operandů při předávání parametrů volaným metodám

9. Odkazy na Internetu

1. Virtuální stroj Javy – JVM

V dnešní části seriálu o programovacím jazyce Java i o vlastnostech JVM se zaměříme na popis virtuálního stroje Javy, který – jak již jeho název ostatně napovídá – vytváří abstraktní (či virtuální) procesor zcela nezávislý na konkrétní architektuře počítače, v němž jsou spouštěny instrukce uložené v bajtkódu jednotlivých tříd. Struktura i vlastnosti virtuálního stroje Javy jsou přesně popsány ve specifikaci JVM, včetně přesného formátu všech datových typů (znaků, celých čísel, čísel s plovoucí řádovou tečkou). Díky dodržování této specifikace různými výrobci virtuálního stroje Javy je zaručeno, že programy napsané v Javě jsou přenositelné na počítače s mnohdy velmi rozdílnou architekturou: od smartphonů s jednojádrovými mikroprocesory až po superpočítače s velkým množstvím výpočetních uzlů a mnohdy s několika desítkami tisíc procesorových jader. Různé nekompatibility, s nimiž programátoři bojovali především v minulosti (ale samozřejmě i dnes, i když v mnohem menší míře), bývají způsobeny buď chybami v implementaci některého virtuálního stroje (a nedodržení specifikace lze považovat za chybu) či nekompatibilitami ve standardních knihovnách, popř. chybami v nativních knihovnách, které lze z JVM volat.

Specifikace virtuálního stroje Javy popisuje některé jeho části velmi precizně (například se jedná o již zmíněné datové typy či o formát instrukcí nebo o strukturu zásobníkového rámce), ovšem u některých dalších částí jsou popsány jen základní vlastnosti a zůstává pouze na tvůrci konkrétní JVM, jakým způsobem se bude daná část virtuálního stroje ve skutečnosti implementovat. Asi nejtypičtějším příkladem je specifikace haldy (heap), u níž se sice předpokládá využití automatické správy paměti, ale nikde již není řečeno, jaký konkrétní algoritmus správy paměti se má použít (v současnosti se využívá hned několik algoritmů) ani jaký formát má být použit pro ukládání referencí na objekty. Díky tomu bylo možné Javu jednoduše přenést z původní 32bitové architektury jak na 16bitové procesory, tak i na procesory pracující s 64bitovými ukazateli (u nichž lze navíc v případě potřeby využít i komprimované ukazatele na objekty) – to vše bez nutnosti zásahu do zdrojových kódů Javovských programů i bez nutnosti jejich rekompilace.

2. Interprety bajtkódu versus just-in-time překladače

Virtuální stroj jazyka Java samozřejmě využívá jiný soubor „strojových“ instrukcí, než fyzický mikroprocesor, na němž jsou Javovské programy spouštěny. Dokonce ani mikroprocesory určené pro přímé spouštění bajtkódu – dnes již zpola zapomenuté projekty MicroJava a PicoJava – nedokázaly nativně spouštět všechny instrukce bajtkódu. V prvních několika letech existence Javy byly instrukce bajtkódu (tvořící těla jednotlivých metod) v naprosté většině případů pouze interpretovány, a to mnohdy velmi jednoduchým způsobem: v programové smyčce se postupně načítaly kódy jednotlivých instrukcí a následně se pro každou instrukci zavolala nativní funkce, která danou instrukci vykonala, většinou s parametry uloženými v zásobníkovém rámci nebo v zásobníku operandů (bližší popis bude uveden v následujících kapitolách). Naprogramování naivního interpretru pro JVM není příliš složité, protože samotná instrukční sada je poměrně jednoduchá. Moderní interpretry, například v tomto seriálu popsaný JamVM, jsou navíc naprogramovány takovým způsobem, aby byl jejich přenos na další procesorové architektury snadný a přitom se bajtkód interpretoval co nejefektivnějším způsobem, ideálně s ručně optimalizovanou vnitřní smyčkou (psanou v assembleru).

Ovšem i přes veškerou snahu programátorů interpretrů nemůže běh programů napsaných v Javě a spouštěných v JVM s interpretovaným bajtkódem v rychlosti soutěžit s nativními aplikacemi. Právě z tohoto důvodu vznikly takzvané just-in-time (JIT) překladače, které dokážou přeložit buď jen určitou sekvenci instrukcí JVM nebo i celý bajtkód do nativního strojového kódu, který je následně spuštěn. Některé JIT překladače, například stále populární HotSpot původně vyvinutý společností Sun Microsystems, používají adaptivní překlad, v němž je bajtkód analyzován v době běhu, takže má JIT překladač k dispozici mnohem více informací, než při statickém překladu. HotSpot navíc umožňuje pomocí takzvaný sond (probes) sledo­vat, který kód je překládán a jaký typ překladu je přitom použit, to je však již poměrně pokročilá a nepříliš často používaná technologie. Ovšem nezávisle na tom, zda jsou instrukce JVM „pouze“ interpretovány, nebo je nejdříve použit JIT, musí být vždy dodrženy všechny vlastnosti jazyka Java i JVM. JIT například může odstranit kontrolu mezí při indexování polí, ale jen tehdy, když je zřejmé, že nedojde k překročení těchto mezí.

3. Programátorský pohled na virtuální stroj Javy

Na virtuální stroj Javy se můžeme dívat způsobem, který se příliš nebude lišit od pohledu na programátorský model jakéhokoli reálného mikroprocesoru. Při inicializaci JVM totiž automaticky dojde k vytvoření paměťových oblastí, které jsou následně využívány Javovským programem. Do tvorby těchto oblastí nemůže samotný Javovský program příliš zasahovat, neboť se jedná o paměť spravovanou mimo vlastní virtuální stroj (to například znamená, že programátor či administrátor systému může zvolit maximální kapacitu paměti pro JVM, velikost zásobníku atd.). Instrukce uložené v bajtkódu nedělají (poněkud nadneseně řečeno) nic jiného, než manipulaci s daty uloženými v jedné nebo více paměťových oblastech, popř. navíc mohou volat další metody, ať již se jedná o metody ze stejné třídy (popř. její instance) či o metody ze třídy jiné. V následující tabulce jsou uvedeny všechny významné paměťové oblasti i další datové struktury, s nimiž se podrobněji setkáme v navazujících kapitolách:

# Název oblasti/struktury Stručný popis oblasti či struktury
1 halda (heap) společný paměťový prostor pro všechna vlákna, obsahuje především data objektů, spravováno pomocí GC
2 method area obsahuje runtime constant pool, kód metod, atributy tříd atd.
3 zásobník (stack) vytvářený pro každé vlákno, skládá se ze zásobníkových rámců (stack frames)
4 registr PC vytvářený pro každé vlákno, ukazuje na právě zpracovávanou instrukci JVM
5 native method stack vytvářený pro každé vlákno a používaný při volání nativních metod (předávání parametrů atd.)

Výše uvedené paměťové oblasti můžeme rozdělit do dvou skupin. V první skupině se nachází ty oblasti, které jsou dostupné celému procesu, ve druhé skupině pak paměťové oblasti vytvářené samostatně pro každé vlákno (thread).

4. Sdílené paměťové prostory: halda (heap), method area a runtime constant pool

Nejprve se budeme zabývat datovými oblastmi společnými pro všechna vlákna běžící v rámci jednoho procesu. Jedná se především o haldu (heap), na níž jsou ukládána zejména data patřící k instancím jednotlivých tříd. Data uložená na haldě bývají spravována některým typem automatického správce paměti (garbage collector – GC), ovšem vzhledem k tomu, že nelze získat přímý ukazatel na objekt ležící na haldě, nás detaily v tomto případě nemusí zajímat. Jak uvidíme dále, přistupuje se k datům na haldě nepřímo, většinou s využitím jména atributu, přičemž jméno atributu je uloženo v constant poolu, takže se přímo v instrukční sadě používají indexy směřující do constant poolu, nikoli přímé adresy objektů na haldě. Součástí haldy může být i takzvaná method area obsahující atributy tříd, kódy jednotlivých metod (instrukce JVM) i obraz constant poolu načtený z bajtkódu. Obraz constant poolu je vytvářen pro každou třídu.

Prozatím se, bez podrobnějšího popisu jednotlivých instrukcí podívejme, jakým způsobem se nepřímo – s využitím jména, nikoli adresy – může volat statická metoda z jiné třídy (volání nestatické metody je nepatrně složitější). Mějme následující dvojici tříd, z nichž každá obsahuje jen jednu metodu:

class OtherClass {
    public static void testMethod() {
    }
}
class Test {
    void callTestMethod() {
        OtherClass.testMethod();
    }
}

Metoda callTestMethod(), která volá statickou metodu OtherClass.tes­tMethod() se přeloží následujícím způsobem:

   0:   invokestatic #2
   3:   return

Instrukce invokestatic (kterou si podrobněji popíšeme příště, prozatím ji považujme za jakousi vylepšenou instrukci typu call) obsahuje jeden operand – index do constant poolu. Podívejme se tedy, jak vypadá constant pool a zvýrazněme si, jaké informace jsou předány JVM při zavolání instrukce invokestatic #2:

Velikost const. poolu: 18 prvku
  1   10  MethodRef         4     12             java/lang/Object.()V
* 2 * 10  MethodRef        13--.  14----.        OtherClass.testMethod()V
  3    7  Class            15  |        |        Test
  4    7  Class            16  |        |        java/lang/Object
  5    1  String               |        |        "<init>"
  6    1  String         ,->   |        |        "()V"
  7    1  String         |     |        |        "Code"
  8    1  String         |     |        |        "LineNumberTable"
  9    1  String         |     |        |        "callTestMethod"
 10    1  String         |     |        |        "SourceFile"
 11    1  String         |     |        |        "Test.java"
 12   12  Name and type  |  6  |   5    |        ()V  <init>
 13    7  Class          | 17  `---------------> OtherClass
 14   12  Name and type  `--6     18--. `------> ()V  testMethod
 15    1  String                      |          "Test"
 16    1  String                      |          "java/lang/Object"
 17    1  String                      |          "OtherClass"
 18    1  String                      `--------> "testMethod"

Instrukci invokestatic přitom musí být předán index na záznam typu MethodRef, což je kontrolováno již při načítání bajtkódu do virtuálního stroje.

5. Paměťový prostor přidělený každému vláknu: zásobník, zásobníkové rámce (stack frames)

Druhou skupinu paměťových oblastí tvoří ty oblasti, které jsou přidělované jednotlivým vláknům. Jedná se především o zásobník (stack), který je tvořen sekvencí zásobníkových rámců (stack frame(s)). Označení „zásobník“ sice může navozovat pocit, že se jedná o kontinuální oblast paměti, ve skutečnosti však mezi jednotlivými zásobníkovými rámci neexistují žádné přímé vazby a proto se každý zásobníkový rámec může nacházet v paměti kdekoli – rámce například mohou být vytvářeny přímo na haldě (záleží na konkrétní implementaci JVM). Rámce jsou tvořeny lokálními proměnnými a parametry metod. Jejich součástí je však taktéž zásobník operandů (operand stack) popsaný v sedmé kapitole. Opět platí, že není možné získat přímou adresu lokálních proměnných, parametrů metod ani dat uložených na zásobníku operandů, proto se nemusí jednat o kontinuální oblast paměti.

Dále zde můžeme najít takzvaný native method stack použitý při volání nativních metod. Poslední datovou strukturou přiřazenou k vláknu, je čítač instrukcí PC, který ukazuje na právě prováděnou instrukci JVM. Při volání nativní metody není tento čítač instrukcí použit (jméno PC je sice shodné s registrem PC, i funkce je podobná, ale konkrétní podoba adresy instrukce JVM je obecně odlišná). V předchozím odstavci bylo řečeno, že v zásobníkovém rámci jsou uloženy jak parametry metod, tak i její lokální proměnné. Nejdříve jsou v zásobníkovém rámci uloženy parametry metod (včetně parametru this u nestatických metod) a ihned za nimi je oblast vyhrazená pro lokální proměnné. Ke všem těmto údajům, tj. jak k parametrům, tak i k lokálním proměnným, se přistupuje pomocí indexu: první údaj má index 0, další 1 atd. Ukažme si to na jednoduchém příkladu třídy s dvojicí metod – jedné nestatické a druhé statické:

class Test {
    void add1(int x, int y) {
        int z = x + y;
    }
 
    static void add2(int x, int y) {
        int z = x + y;
    }
}

Obě metody sice provádí tutéž činnost – součet dvou celých čísel s uložením výsledku do lokální proměnné – ovšem způsob indexování parametrů je uvnitř JVM odlišný, protože v nestatické metodě je v první pozici zásobníkového rámce (tato pozice má index roven nule) uložen implicitní parametr this, kdežto v metodě statické se na stejné pozici zásobníkového rámce nachází již první parametr:

void add1(int, int);
  Code:
   Stack=2, Locals=4, Args_size=3
                        // v prvním parametru je uložena hodnota this, tu nepotřebujeme
   0:   iload_1         // načtení druhého parametru metody s jeho uložením na zásobník operandů
   1:   iload_2         // načtení třetího parametru metody s jeho uložením na zásobník operandů
   2:   iadd            // provedení součtu
   3:   istore_3        // uložení výsledku na čtvrtou pozici v zásobníkovém rámci: první lokální proměnné
   4:   return          // návrat z metody
static void add2(int, int);
  Code:
   Stack=2, Locals=3, Args_size=2
   0:   iload_0         // načtení prvního parametru metody s jeho uložením na zásobník operandů
   1:   iload_1         // načtení druhého parametru metody s jeho uložením na zásobník operandů
   2:   iadd            // provedení součtu
   3:   istore_2        // uložení výsledku na třetí pozici v zásobníkovém rámci: první lokální proměnné
   4:   return          // návrat z metody

Přesný význam všech instrukcí použitých ve výpisu si uvedeme příště.

6. Datové typy zpracovávané virtuálním strojem Javy

Virtuální stroj jazyka Java obsahuje instrukce, které pracují s operandy několika datových typů. Na rozdíl od mnoha fyzických procesorů se v případě JVM provádí kontroly, zda jsou operace skutečně aplikovány na správné operandy. Není například možné, aby se operace součtu prováděla s jedním operandem typu int a druhým operandem typu long – takový bajtkód by byl při svém načítání odmítnut a vůbec by nebyl spuštěn. Zajímavé je, že jen velmi málo instrukcí JVM podporuje práci s datovými typy boolean, byte, short a char. Proměnné a parametry metod těchto typů musí být například před provedením některé aritmetické operace nejprve převedeny na typ int pomocí konverzních instrukcí (těch existuje celkem patnáct). Mimochodem – tato vlastnost bajtkódu a virtuálního stroje se projevuje i v sémantice vlastního programovacího jazyka Java. Příkladem může být následující metoda:

    byte byteAdd(byte a, byte b) {
        byte c=a+b;
        return c;
    }

Při pokusu o překlad této metody (resp. třídy s touto metodou) získáme pouze chybové hlášení:

% javac Test.java
Test.java:3: possible loss of precision
found   : int
required: byte
        byte c=(a+b);
                 ^
1 error

Z toho je patrné, že datový typ byte (popř. i short a char) není „uzavřený“ vůči aritmetickým operacím (operandy jsou automaticky zkonvertovány na int) a je nutné provést explicitní přetypování:

    static byte byteAdd(byte a, byte b) {
        byte c=(byte)(a+b);
        return c;
    }

Vraťme se však k virtuálnímu stroji Javy. Ten dokáže pracovat s celkem deseti datovými typy, které jsou všechny vypsány v následující tabulce. Prvních osm datových typů odpovídá primitivním datovým typům známým všem programátorům v Javě, devátý typ odpovídá objektovému datovému typu (reference na libovolnou instanci), ovšem desátý typ není přímo z Javy přístupný. Operand s tímto typem obsahuje ukazatel na instrukční kód, tj. může se například jednat o skutečnou adresu v adresovém prostoru procesoru s JVM, offset platný v rámci prostoru haldy atd. (to stejné ostatně platí i pro registr PC zmíněný v páté kapitole).

# Typ v Javě Použitý typ v JVM
1 boolean int
2 byte int
3 char int
4 short int
5 int int
6 long long
7 float float
8 double double
9 reference reference
10 není dostupný returnAddress

7. Zásobník operandů

Většina instrukcí virtuálního stroje Javy pracuje s operandy uloženými na takzvaném zásobníku operandů (operand stack). Zásobník operandů (v tomto případě se již jedná o skutečný zásobník typu LIFO – Last In, First Out) je vytvářen v čase běhu aplikace pro každou zavolanou metodu, což mj. znamená, že je při spuštění metody vždy prázdný (zásobník operandů je podle specifikace součástí zásobníkového rámce, jeho konkrétní umístění však je libovolné). Již v čase překladu zdrojového kódu je pro každou metodu zjištěno, jak velká oblast paměti má být pro zásobník operandů vyhrazena a samozřejmě je prováděna kontrola, zda se v době běhu aplikace tato velikost nepřekročí (to by se nemělo u validního bajtkódu stát). Virtuální stroj Javy kontroluje typy operandů uložených na zásobník operandů a zajišťuje, že se nad těmito operandy budou provádět pouze typově bezpečné operace. V praxi to například znamená, že není možné na zásobník uložit dvě hodnoty typu float a následně provést instrukci iadd, protože tato instrukce vyžaduje, aby na zásobníku byly uloženy dvě hodnoty typu int (i když floatint mají shodnou bitovou šířku).

Podívejme se na jednoduchou ukázku využití zásobníku operandů při vyhodnocování složitějšího aritmetického výrazu:

class Test {
    void test() {
        int a=10;
        int b=20;
        int c=30;
        int d=30;
        int z=a+b*(c-d);
    }
}

Přeložený bajtkód metody Test.test() je následující:

void test();
  Code:
   0:   bipush  10
   2:   istore_1           // naplnění proměnné a hodnotou 10
   3:   bipush  20
   5:   istore_2           // naplnění proměnné b hodnotou 20
   6:   bipush  30
   8:   istore_3           // naplnění proměnné c hodnotou 30
   9:   bipush  30
   11:  istore  4          // naplnění proměnné d hodnotou 40
 
   13:  iload_1            // vložení a na zásobník      [a]
   14:  iload_2            // vložení b na zásobník      [a b]
   15:  iload_3            // vložení c na zásobník      [a b c]
   16:  iload   4          // vložení d na zásobník      [a b c d]
   18:  isub               // provedení operace x=c-d    [a b c-d]
   19:  imul               // provedení operace y=b*x    [a b*(c-d)]
   20:  iadd               // provedení operace a+y      [a+b*(c-d)]
   21:  istore  5          // uložení výsledku do z      []
 
   23:  return
}

8. Využití zásobníku operandů při předávání parametrů volaným metodám

Zásobník operandů je taktéž použit při volání metod pro předání parametrů těmto metodám a pro vyzvednutí jejich návratových hodnot. Záleží jen na konkrétní implementaci virtuálního stroje Javy, jakým způsobem dojde při volání metody ke konverzi mezi zásobníkem operandů volající metody a lokálními parametry metody volané. Ukažme si způsob použití zásobníku operandů jak pro provádění aritmetické operace součtu, tak i pro předání parametrů volající metodě a získání výsledku z volané metody. Mějme jednoduchou třídu s dvojicí metod:

widgety

class Test {
    // provedení součtu dvou celých čísel
    static int add(int a, int b) {
        int c=a+b;
        return c;
    }
 
    // zavolá metodu pro provedení součtu dvou čísel
    // a uloží návratovou hodnotu do své lokální proměnné
    static void callAdd() {
        int result = add(1234,5678);
    }
}

Přeložený bajtkód obou metod je následující. Všechny poznámky jsou samozřejmě dopsány ručně:

// v metodě add je zásobník operandů použit pouze pro provedení operace součtu
static int add(int, int);
  Code:
   0:   iload_0         // uložení prvního parametru metody na zásobník operandů
   1:   iload_1         // uložení druhého parametru metody na zásobník operandů
   2:   iadd            // provedení operace součtu s odstraněním obou operandů
   3:   istore_2        // vyzvednutí výsledku součtu a uložení do lokální proměnné
   4:   iload_2         // opětovné uložení obsahu lokální proměnné na zásobník
   5:   ireturn         // při operaci ireturn se využije hodnota z vrcholu zásobníku
// v této metodě se zásobník operandů používá i pro komunikaci s metodou Test.add(int, int)
static void callAdd();
  Code:
   0:   sipush  1234    // uložení konstanty 1234 na zásobník operandů
   3:   sipush  5678    // uložení konstanty 5678 na zásobník operandů
   6:   invokestatic #2 // zavolání statické metody Test.add(int, int)
   9:   istore_0        // výsledná hodnota je umístěna na vrchol zásobníku operandů,
                        // tak ji odtud vyzvedneme a uložíme do lokální proměnné
   10:  return          // návrat z metody

Povšimněte si, že překladač javac neprovedl žádné optimalizace, i když se nám například může zdát dvojice po sobě jdoucích instrukcí istore2 a iload2 nadbytečná (ve skutečnosti je to nutné pro správnou funkci debuggeru).

9. Odkazy na Internetu

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

Dostal malý pivovar ze Slovenska do Tesca

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje

120na80.cz: Hrbatá prsa aneb mýty o implantátech

Hrbatá prsa aneb mýty o implantátech

Vitalia.cz: Test dětských svačinek: Tyhle ne!

Test dětských svačinek: Tyhle ne!

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Podnikatel.cz: Chystá se smršť legislativních novinek

Chystá se smršť legislativních novinek

Vitalia.cz: 5 pravidel proti infekci močových cest

5 pravidel proti infekci močových cest

Vitalia.cz: Voda z Vltavy před a po úpravě na pitnou

Voda z Vltavy před a po úpravě na pitnou

Podnikatel.cz: Byla finanční manažerka, teď cvičí jógu

Byla finanční manažerka, teď cvičí jógu

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

DigiZone.cz: Numan Two: rozhlasový přijímač s CD

Numan Two: rozhlasový přijímač s CD

DigiZone.cz: O2 Sport zbrojí na derby pražských S

O2 Sport zbrojí na derby pražských S

Vitalia.cz: Inspekce našla nelegální sklad v SAPĚ. Zase

Inspekce našla nelegální sklad v SAPĚ. Zase

Vitalia.cz: Jaký je rozdíl mezi brambůrky a chipsy?

Jaký je rozdíl mezi brambůrky a chipsy?