Obsah
1. Malé zopakování z minula – objekty typu „futures“
2. Další funkce a makra používaná při práci s objekty typu „futures“
4. Nebezpečí, které hrozí při práci s objekty typu „promise“
5. Architektura dataflow realizovaná s využitím objektů typu „promise“
6. Technologie STM – Software Transactional Memory
7. Funkce a makra používaná při práci s transakcemi
8. Demonstrační příklad: transakce při převodu peněz mezi dvěma účty
1. Malé zopakování z minula – objekty typu „futures“
V předchozí části seriálu o Javě i o vlastnostech virtuálního stroje Javy jsme si mj. popsali i takzvané objekty typu futures, které představují jednu z několika technologií programovacího jazyka Clojure vyvinutých pro snadnou tvorbu vícevláknových aplikací. Připomeňme si, že s využitím objektů typu futures je možné spustit nějaký výpočet (obecně se jedná o vyhodnocení funkce, zavolání makra či o zavolání speciální formy), který je prováděn asynchronně v samostatném vlákně, tj. tak, že hlavní vlákno může ihned pokračovat v provádění dalších příkazů a nemusí čekat na dokončení tohoto výpočtu. V případě, že asynchronní výpočet skončí dřív, než je zapotřebí znát jeho výsledek, nemusí vůbec dojít k synchronizaci vláken, ovšem ve chvíli, kdy se programátor dotazuje na výsledek asynchronního výpočtu, který ještě není dokončen (tj. při čtení výsledku s použitím funkce deref nebo makra @), je hlavní vlákno programu pozastaveno tak, aby se počkalo na dokončení výpočtu.
Díky tomuto chování je práce s objekty typu futures velmi snadná a bezpečná (tj. při správné metodě programování by nemělo dojít k deadlockům, „vyhladovění/starvation“ vláken atd.). Jednoduchý program spouštějící výpočty v asynchronně běžících samostatných vláknech může vypadat například takto (podrobnější popis viz předchozí část seriálu):
; funkce pro velmi pomalý výpočet členů Fibonacciho řady (defn fibonacci [n] (if (> n 2) n (+ (fibonacci (- n 2)) (fibonacci (- n 1))))) ; namísto přímého volání funkce fibonacci však spustíme ; dva asynchronní výpočty s využitím (future funkce) user=> (def future_fibonacci1 (future (fibonacci 35))) #'user/future_fibonacci1 user=> (def future_fibonacci2 (future (fibonacci 35))) #'user/future_fibonacci2 ; oba výpočty jsou nyní prováděny ve svých vláknech na pozadí ; - lze se o tom přesvědčit například programem "top" ; nebo s využitím JConsole/VisualVM spuštěné nad interpretrem ; programovacího jazyka Clojure ; nyní si explicitně vyžádáme výsledek výpočtu ; - zde již může být hlavní vlákno pozastaveno, ; aby mohl být výpočet dokončen user=> @future_fibonacci1 9227465 user=> @future_fibonacci2 9227465
2. Další funkce a makra používaná při práci s objekty typu „futures“
V mnoha případech můžeme vystačit pouze s použitím makra future (viz též demonstrační příklad uvedený v předchozí kapitole) s tím, že pro získání výsledku výpočtu bude sloužit funkce deref, která se velmi často v programech zapisuje pouze pomocí zavináče @ (v tomto případě jde o makro zabudované do preprocesoru). Ovšem ve složitějších aplikacích je mnohdy nutné se explicitně dotázat na to, zda již asynchronní výpočet skončil. Tento dotaz by měl proběhnout bez toho, aby se hlavní vlákno pozastavilo, popř. taktéž mohou nastat situace, které si vynutí ukončení jednoho či více těchto výpočtů. Pro všechny podobné situace samozřejmě programovací jazyk Clojure nabízí odpovídající funkce či makra. Dotaz na stav výpočtu se provádí s využitím funkce realized?. Jak již z názvu této funkce (otazník na konci) vyplývá, jedná se o predikát vracející pravdivostní hodnotu true nebo false v závislosti na tom, jestli byl asynchronní výpočet představovaný objektem typu future již ukončen či nikoli.
Podívejme se nyní na demonstrační příklad, v němž se aktivně (tudíž nepříliš chytře :-) čeká na dokončení vlákna s výpočtem:
; rekurzivní zápis funkce pro výpočet jednoho členu ; Fibonacciho posloupnosti (defn fibonacci [n] (if (> n 2) n (+ (fibonacci (- n 2)) (fibonacci (- n 1))))) ; pomocná funkce, která pozastaví aktivní ; vlákno na přibližně jednu sekundu (defn wait-one-sec [] (Thread/sleep 1000)) ; vytvoření future objektu s asynchronním výpočtem (def future_fibonacci1 (future (fibonacci 40))) ; aktivní čekání na dokončení výpočtu (while (not (realized? future_fibonacci1)) (println "Cekam na konec vypoctu... ") (wait-one-sec) ) ; tisk výsledku výpočtu (println "Vysledek: " @future_fibonacci1)
Podobným způsobem je možné použít i predikát future-done?. Ten je možné aplikovat pouze na objekty typu future, zatímco predikát realized? je možné aplikovat i na lazy sekvence atd.:
; vytvoření future objektu s asynchronním výpočtem (def future_fibonacci1 (future (fibonacci 40))) ; aktivní čekání na dokončení výpočtu (while (not (future-done? future_fibonacci1)) (println "Cekam na konec vypoctu... ") (wait-one-sec) ) ; tisk výsledku výpočtu (println "Vysledek: " @future_fibonacci1)
Asynchronní výpočet lze taktéž násilně ukončit funkcí future-cancel. Pokud k ukončení výpočtu skutečně dojde, vrátí tato funkce pravdivostní hodnotu true, v opačném případě (výpočet již byl ukončen) se vrátí pravdivostní hodnota false. Opět se podívejme na jednoduchý demonstrační příklad:
; vytvoření future objektu s asynchronním výpočtem (def future_fibonacci1 (future (fibonacci 100))) #'user/future_fibonacci1 ; čekání jednu sekundu (výpočet by ještě neměl být ukončen) (wait-one-sec) nil ; násilné ukončení asynchronního výpočtu (future-cancel future_fibonacci1) true ; při druhém pokusu o násilné ukončení ; asynchronního výpočtu se pouze vrátí hodnota false (future-cancel future_fibonacci1) false
3. Objekty typu „promise“
S objekty typu future dosti úzce souvisí i funkce deliver a objekty typu promise, které mohou být použity pro uložení výsledku výpočtu do zvolené proměnné (stále se sice bavíme o výpočtu, ale může se samozřejmě jednat o jakoukoli dlouhotrvající operaci, například zavolání webové služby, čtení dat z SQL databáze atd. atd.). V programovacím jazyku Clojure představují objekty typu promise takové hodnoty, které ještě nemusí v době své deklarace vůbec existovat, tj. může se jednat o hodnoty, jež teprve vzniknou, typicky na základě asynchronně běžícího výpočtu (použití objektů typu promise v jednovláknovém programu je sice možné, ale většinou zbytečně komplikované a současně i nebezpečné). S těmito objekty lze provést pouze dvě činnosti – může se jim nastavit nějaká hodnota (a to pouze jedenkrát, nelze ji potom změnit) a může se přečíst, přesněji řečeno dereferencovat, jejich hodnota. Důležité je, že pokud tato hodnota ještě neexistuje (tj. asynchronní výpočet se ještě nedokončil), je vlákno, které hodnotu dereferencuje (čte) pozastaveno na tak dlouho, dokud není hodnota objektu typu promise skutečně nastavena. Ihned poté, co je hodnota nastavena, mohou k této hodnotě ihned přistupovat i všechna ostatní vlákna.
Práce s objekty typu promise je ve skutečnosti velmi jednoduchá. Nejprve se objekt promise vytvoří zavoláním funkce promise (která nemá žádné parametry) a samozřejmě se tento objekt může navázat na libovolný symbol s využitím speciální formy def, tj. takto: (def symbol (promise)). Hodnotu lze objektu typu promise přiřadit s využitím funkce deliver, které se předá jak symbol navázaný na objekt promise, tak i hodnota, jenž se má k objektu přiřadit: (deliver jméno-objektu-promise hodnota). Od této chvíle mohou tuto hodnotu používat všechna vlákna, která k ní mohou přistupovat buď s využitím funkce (deref jméno-objektu-promise) nebo zkráceně @jméno-objektu-promise (opět se zde setkáváme s makrem @ vytvořeným v preprocesoru). V následujícím demonstračním příkladu je vytvořen objekt typu promise, jenž je navázán na symbol promise-test. Následně je tomuto objektu přiřazena hodnota 42, která je následně vypsána. Všechny tyto činnosti se provádí v jediném vláknu, nedochází zde tedy k vytvoření samostatného vlákna pro „výpočet“:
; vytvoření objektu typu promise ; a navázání na symbol promise-test (def promise-test (promise)) #'user/promise-test ; nastavení hodnoty (deliver promise-test 42) #<core$promise$reify__6153@1318b: 42> ; získání nastavené hodnoty @promise-test 42
4. Nebezpečí, které hrozí při práci s objekty typu „promise“
Důležitý je taktéž již v předešlé kapitole zmíněný fakt, že hodnotu do objektu typu promise je možné nastavit pouze jedenkrát, tj. po nastavení ji již nelze změnit. Další pokusy o nastavení jiné hodnoty jsou jazykem Clojure ignorovány (v některých verzích se navíc při pokusu o přepis již nastavené hodnoty vyvolá výjimka – je nutné otestovat). Opět si to můžeme ukázat na jednoduchém demonstračním příkladu, kdy se do promise-test nejprve uloží hodnota 42 a poté se snažíme do stejného objektu typu promise uložit odlišnou hodnotu: 100. To se nepovede, takže výraz @promise-test je vyhodnocen na 42:
; vytvoření objektu typu promise ; a navázání na symbol promise-test (def promise-test (promise)) #'user/promise-test ; nastavení hodnoty (deliver promise-test 42) #<core$promise$reify__6153@1318b: 42> ; pokus o nové nastavení hodnoty (deliver promise-test 100) nil ; získání nastavené hodnoty @promise-test 42 ; vidíme, že se vrátila prvně nastavená hodnota
Při práci s objekty typu promise hrozí jedno velké nebezpečí, s nímž jsme se vlastně při popisu vlastností programovacího jazyka Clojure ještě nesetkali – v případě, že se do tohoto objektu nepřiřadí žádná hodnota s využitím funkce deliver a některé vlákno se bude snažit přečíst hodnotu přiřazenou k objektu promise, nastane deadlock. Příklad deadlocku je v tomto případě až neskutečně jednoduchý, protože pouze postačuje vytvořit objekt typu promise a ihned poté se v tomtéž vlákně snažit přečíst k němu nastavenou hodnotu. To se samozřejmě nepovede, takže smyčka REPL bude v deadlocku čekat, jestli náhodou jiné vlákno přece jen kýženou hodnotu nenastaví:
; vytvoření objektu typu promise ; a navázání na symbol promise-test (def promise-test (promise)) #'user/promise-test ; pokus o čtení nenastavené hodnoty @promise-test *** deadlock ***
V některých případech je nutné před čtením hodnoty přiřazené k objektu typu promise zjistit, zda je tomuto objektu hodnota již přiřazena. Zde nám pomůže známý predikát realized?, viz též následující demonstrační příklad:
; vytvoření objektu typu promise ; a navázání na symbol promise-test (def promise-test (promise)) #'user/promise-test ; test, zda je hodnota již přiřazena (realized? promise-test) false ; ... samozřejmě ještě není ; nastavení hodnoty (deliver promise-test 42) #<core$promise$reify__6153@1318b: 42> ; další test, zda je hodnota již přiřazena (realized? promise-test) true ; ... nyní již jsme úspěšnější @promise-test 42
5. Architektura dataflow realizovaná s využitím objektů typu „promise“
Po přečtení předchozích dvou kapitol by se možná mohlo zdát, že objekty typu promise přináší při tvorbě reálných aplikací jen problémy a neměly by se vlastně vůbec používat. Ve skutečnosti se však může jednat o velmi užitečnou technologii, jež umožňuje tvořit aplikace poněkud jiným způsobem, než je tomu obvykle zvykem. Většina dnešních programovacích jazyků je totiž založena na imperativním způsobu zápisu programů, v nichž se explicitně zapisuje tok běhu programu: sekvence příkazů, rozvětvení programu, programové smyčky, rekurze. Alternativně je ovšem možné využít i způsob, který můžeme nalézt v systémech s architekturou dataflow – v těchto systémech se (dosti zjednodušeně řečeno) popisuje, co se má stát ve chvíli, kdy do nějakého funkčního bloku přijdou všechny potřebné vstupní údaje a už se většinou explicitně neuvádí, v jakém pořadí jsou jednotlivé funkční bloky vykonávány. Čistě teoreticky mohou všechny funkční bloky pracovat paralelně, což je také jeden z důvodů, proč se v minulosti dataflow systémům věnovala poměrně velká pozornost.
V současnosti lze za dataflow systém považovat například grafický akcelerátor, ovšem právě s použitím objektů typu promise a taktéž s využitím objektů future je možné podobný systém vytvořit i v jazyku Clojure. Ostatně podívejme se na jednoduchý demonstrační příklad:
; funkce pro velmi pomalý výpočet členů Fibonacciho řady (defn fibonacci [n] (if (> n 2) n (+ (fibonacci (- n 2)) (fibonacci (- n 1))))) ; vytvoření tří objektů typu promise (def x (promise)) (def y (promise)) (def z (promise)) ; tři asynchronně běžící výpočty, ; v nichž se postupně (někdy v budoucnu) ; nastaví hodnoty všech tří objektů ; typu promise (def task-1 (future (deliver z (+ @x @y)))) (def task-2 (future (deliver x (fibonacci 35)))) (def task-3 (future (deliver y (fibonacci 40)))) ; výpočty nyní běží, přičemž ve skutečnosti první ; výpočet musí čekat na dokončení ostatních dvou ; asynchronních vláken ; kdykoli se můžeme dotázat na @x, @y či @z
6. Technologie STM – Software Transactional Memory
S využitím objektů typu future je možné, jak již po přečtení předchozích odstavců víme, velmi snadno realizovat asynchronně běžící výpočty. Podobně je tomu i v případě takzvaných agentů, s nimiž se podrobněji seznámíme v následující části tohoto seriálu. Při programování reálných vícevláknových aplikací se však často setkáme s problémy, které lze vyřešit buď složitě s využitím zámků či synchronizovaných bloků (což je postup používaný velmi často v Javě, která pro tyto účely má vyhrazeno i klíčové slovo), nebo jednodušší cestou – pomocí transakcí. Jednou z důležitých technologií, kterou programovací jazyk Clojure vývojářům nabízí, je totiž i takzvaná STM neboli „Software Transactional Memory“, díky níž lze transakce realizovat. Pod pojmem transakce rozumíme takovou operaci, která se z hlediska ostatních vláken provede atomicky, tj. v jeden okamžik. Současně je transakce prováděna izolovaně; operace prováděné uvnitř transakce (změna některých referencí atd.) není viditelná zvenku až do chvíle, kdy je transakce ukončena. Taktéž je možné spustit transakci uvnitř jiné transakce – vnitřní transakce v tomto případě provede commit–uložení výsledků až současně s transakcí vnější (což je očekávatelné chování). Obecně je možné říci, že transakce v Clojure splňují vlastnost ACID (viz též databázové transakce), s tím, že o konzistenci se musí postarat sám programátor.
Transakce implementované v programovacím jazyce Clojure navíc nepoužívají žádné zámky a snaží se být při svém spouštění „optimistické“. Co to v praxi vlastně znamená? Pokud je nějaká část kódu vykonávána v transakci, provede se nejprve kopie všech referencí, které jsou uvnitř transakce měněny a všechny operace se následně provádí nad těmito kopiemi (zvnějšku to tedy vypadá tak, že transakce nemění žádné jiné objekty). Navíc se všechny transakce mohou spouštět paralelně a nezávisle na sobě. Co se však stane v případě, že nějaká transakce po svém ukončení zjistí, že jí vypočtené hodnoty, které se snaží commitovat, již byly mezitím změněny? Systém v tomto případě jednoduše transakci spustí znova a využije přitom již změněné hodnoty. V praxi to sice může znamenat, že nastávají situace, kdy bude jedna transakce spuštěna klidně i několikrát, ovšem zde velmi záleží na tom, co konkrétní aplikace vlastně provádí – uvádí se, že obecně je využití transakcí rychlejší, než explicitní použití zámků/synchronizovaných bloků, ovšem může se stát, že aplikace bude mít spuštěno několik tisíc transakcí, které na sobě budou závislé, a zde bude již situace odlišná (popravdě řečeno bych však nechtěl takovou aplikaci využívající zámky/synchronizované bloky ladit).
7. Funkce a makra používané při práci s transakcemi
V transakcích se většinou pracuje nikoli s globálními proměnnými, ale s objekty typu ref. S těmito typy objektů jsme se sice prozatím ještě nesetkali, i když je na nich v podstatě založena filozofie jazyka Clojure, ovšem práce s nimi je z uživatelského hlediska velmi jednoduchá. Objekt typu ref se vytvoří zavoláním funkce ref, které se předá i počáteční hodnota tohoto objektu. Aby se s ref mohlo dále pracovat, navazuje se obvykle na nějaký symbol s využitím speciální formy def (tím se objekt „pojmenuje“). Podívejme se na dvě definice objektu typu ref s navázáním těchto objektů na dvojici globálních symbolů:
user=> (def my-ref (ref 42)) #'user/my-ref user=> (def string-ref (ref "Hello world")) #'user/string-ref
Hodnotu, resp. přesněji řečeno stav objektu ref lze získat s využitím funkce deref:
user=> (deref my-ref) 42 user=> (deref string-ref) "Hello world"
Pro zjednodušení se však v reálných programech namísto volání deref používá spíše makro preprocesoru zapisované pomocí zavináče:
user=> @my-ref 42 user=> @string-ref "Hello world"
Nyní se již konečně dostáváme k použití objektů typu ref v transakcích. Pro změnu stavu těchto objektů slouží funkce ref-set a taktéž funkce alter. Odlišnost mezi těmito funkcemi spočívá v tom, že se pomocí ref-set přímo nastavuje nová hodnota (nový stav ref), zatímco přes alter je hodnotu možné změnit tak, že se na původní stav zavolá vybraná funkce, která samozřejmě může mít i další parametry. Nejprve se podívejme na to, co se stane ve chvíli, kdy se pokusíme zavolat funkci ref-set mimo transakci:
user=> (ref-set my-ref 10) IllegalStateException No transaction running clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208) user=> (ref-set string-ref "xyzzy") IllegalStateException No transaction running clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208)
Je vidět, že Clojure v tomto případě nebyl příliš nadšený :-), jelikož je funkce ref-set určena pro použití uvnitř transakcí a nikoli pro obecné použití mimo transakce. Podobně to dopadne v případě funkce alter:
user=> (alter my-ref + 10) IllegalStateException No transaction running clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208)
Aby se změna stavu objektu ref podařila, musíme ji provést v transakci, která se nejjednodušeji zapisuje pomocí makra dosync. V tomto makru může být uvedeno libovolné množství forem, které se postupně provedou a to takovým způsobem, že zaručí všechny vlastnosti transakcí – tj. klasický ACID:
user=> (dosync (ref-set my-ref 10)) 10
Více příkazů v jedné transakci se zapisuje následujícím způsobem:
(dosync (ref-set my-ref 20) (ref-set string-ref "xyzzy"))
Zbývá nám ukázat funkci alter, která se taktéž musí volat uvnitř transakce:
user=> (dosync (alter my-ref + 10)) 30
Popř:
(dosync (ref-set string-ref "root.cz") (alter my-ref * 2))
Čtení hodnoty (stavu) objektu ref lze provést kdykoli, tj. i mimo transakci:
user=> @string-ref "root.cz" user=> @my-ref 40
Při práci s transakcemi je nutné dbát především na to, aby se do funkce alter předávaly jen takové funkce, které nemají vedlejší efekt. Proč tomu tak musí být? Vzpomeňme si na předchozí kapitolu, kde jsme si řekli, že transakce může být v případě potřeby spuštěna i několikrát za sebou, takže si asi dokážete představit, jaké problémy by funkce s vedlejším efektem mohla v reálných aplikacích způsobit.
8. Demonstrační příklad: transakce při převodu peněz mezi dvěma účty
Typickým příkladem, na němž se velmi často vysvětlují databázové transakce, je funkce (v SQL databázích spíše uložená procedura) provádějící převod peněz z jednoho účtu na druhý. V reálném světě jde z pochopitelných důvodů o dosti choulostivou operaci, u níž je (alespoň z pohledu většiny účastníků :-) žádoucí, aby byly peníze buď skutečně převedeny a obsahy obou účtů náležitě změněny, nebo aby naopak obsahy obou účtů zůstaly nezměněny v případě, kdy by nějaká část transakce z různých důvodů selhala. Podobnou aplikaci s transakcemi můžeme naprogramovat i s využitím programovacího jazyka Clojure a bude se kupodivu jednat o velmi jednoduchý program. Nejprve pro jednoduchost vytvoříme tři globální symboly nazvané account-1, account-2 a account-3, které budou představovat trojici účtů v bance. Na prvním účtu je na začátku aplikace uloženo 10000 Kč, na druhém účtu 20000 Kč a na účtu třetím nejsou prozatím uloženy peníze žádné:
(def account-1 (ref 10000)) (def account-2 (ref 20000)) (def account-3 (ref 0))
Pro další zkrácení programu si nadefinujeme pomocnou funkci nazvanou print-accounts, která slouží k tisku aktuálního obsahu všech tří účtů (obecně však není dobré programovat podobné funkce, které přistupují přímo ke globálním symbolům; berme to jako daň za snahu o co nejkratší program):
(defn print-accounts [] (println "Account 1 : " @account-1) (println "Account 2 : " @account-2) (println "Account 3 : " @account-3))
Nyní již můžeme naprogramovat nejdůležitější a nejzajímavější část celého demonstračního příkladu. Jedná se o funkci nazvanou příhodně transfer-money, která odečte od prvního vybraného účtu (předanému jako první parametr) zadanou sumu a přesně tutéž sumu přičte ke druhému účtu (předanému jako druhý parametr). Tento přesun peněz je proveden v transakci, tj. uvnitř makra dosync. Modifikace obsahu obou účtů zajišťuje funkce alter, která na hodnotu prvního účtu aplikuje funkci – a na hodnotu druhého účtu pak funkci +:
(defn transfer-money [from to amount] (dosync (alter from - amount) (alter to + amount)))
Nyní si již můžeme si celý program otestovat:
(println "Before transactions: ") (print-accounts) Before transactions: Account 1 : 10000 Account 2 : 20000 Account 3 : 0 nil
Počáteční hodnota účtů tedy byla nastavena dobře. Zkusíme provést dvojici transakcí:
(transfer-money account-1 account-2 1000) (transfer-money account-2 account-3 10000)
A vypsat hodnoty účtů po provedení obou transakcí:
(println "After transactions: ") (print-accounts) After transactions: Account 1 : 9000 Account 2 : 11000 Account 3 : 10000 nil
9. Odkazy na Internetu
- Clojure home page
http://clojure.org/downloads - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDocs
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - 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 - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - 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