Obsah
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
8. Využití zásobníku operandů při předávání parametrů volaným metodám
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) sledovat, 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.testMethod() 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ž float i int 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:
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
- Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html