Pohled pod kapotu JVM - sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (2.část)

Pavel Tišnovský 13. 11. 2012

Dnes budeme pokračovat v popisu rozhraní JVM TI, které je možné využít pro tvorbu agentů monitorujících popř. ovlivňujících chování aplikace v JVM. Dnes se budeme zabývat převážně zjišťováním jmen i signatur volaných metod a sledováním správce paměti. Přitom se dozvíme další podrobnosti o rozhraní JVM TI.

Obsah

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (2.část)

2. Alokace a dealokace paměti v agentech využívajících rozhraní JVM TI

3. Zjištění jmen a signatur volaných metod

4. Čtvrtý demonstrační příklad: výpis signatur všech volaných metod

5. Použití zámků v agentech využívajících rozhraní JVM TI

6. Funkce CreateRawMonitor, RawMonitorEnter a RawMonitorExit

7. Pátý demonstrační příklad: agent pro výpis volaných metod využívající zámky

8. Šestý demonstrační příklad: agent registrující práci garbage collectoru

9. Odkazy na Internetu

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (2.část)

V dnešní části seriálu o programovacím jazyku Java i o jeho virtuálním stroji budeme pokračovat v popisu rozhraní JVM TI. Navážeme na předchozí část, v níž byl na konci (osmá kapitola) ukázán JVM TI agent, který dokázal ve virtuálním stroji Javy detekovat volání každé metody. Nyní si řekneme, jak lze tento příklad rozšířit takovým způsobem, aby se vypisovalo i jméno metody a současně i její signatura, což je zajisté mnohem užitečnější. Termínem signatura je zde myšlen jednoznačný zápis počtu a typů parametrů metody i jejího návratového typu. Náš nový agent by měl vypisovat informace o volaných metodách v tomto formátu:

called method method1 with signature (Ljava/lang/String;Ljava/lang/String;)V
called method <init> with signature ()V
called method <init> with signature (I)V
called method <init> with signature ()V
called method append with signature (Ljava/lang/String;)Ljava/lang/StringBuilder;
called method append with signature (Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;
called method length with signature ()I
called method getChars with signature (II[CI)V
called method arraycopy with signature (Ljava/lang/Object;ILjava/lang/Object;II)V
called method append with signature (Ljava/lang/String;)Ljava/lang/StringBuilder;
called method append with signature (Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;
called method length with signature ()I
called method getChars with signature (II[CI)V
called method arraycopy with signature (Ljava/lang/Object;ILjava/lang/Object;II)V
called method append with signature (Ljava/lang/String;)Ljava/lang/StringBuilder;
called method append with signature (Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;
called method length with signature ()I
called method getChars with signature (II[CI)V
called method arraycopy with signature (Ljava/lang/Object;ILjava/lang/Object;II)V
called method toString with signature ()Ljava/lang/String;
called method <init> with signature ([CII)V
called method <init> with signature ()V
called method copyOfRange with signature ([CII)[C
called method min with signature (II)I
called method arraycopy with signature (Ljava/lang/Object;ILjava/lang/Object;II)V
called method method2 with signature (Ljava/lang/String;)V
called method method3 with signature (Ljava/lang/String;)V
called method println with signature (Ljava/lang/String;)V

Jméno metody má jasný význam (ještě ho příště doplníme o jméno příslušné třídy), ovšem signatura metody může v některých případech vypadat poněkud nečitelně. Vzhledem k tomu, že se programátoři se signaturami metod mohou setkat i v dalších částech JVM (v bajtkódu atd.), bude vhodné si ozřejmit, jak se signatury vlastně zapisují. Obecný tvar jejich zápisu je následující:

 
(typy_parametrů)návratový_typ
 

Všechny typy, tj. jak typy parametrů metody, tak i její návratový typ, jsou v případě jednoduchých (primitivních) datových typů zapisovány jedním znakem (viz též následující tabulka). V případě polí se používá znak [, popř. u vícedimenzionálních polí se zapisuje více znaků [ za sebe. Za těmito znaky následuje specifikace typů prvků polí. Pokud je typem třída či rozhraní, je před její jméno zapsán znak L a jméno třídy/rozhraní je ukončeno středníkem:

# Znak/řetězec Datový typ Význam
1 B byte osmibitové celé číslo se znaménkem
2 C char znak reprezentovaný v Unikódu
3 D double číslo s plovoucí řádovou čárkou s dvojitou přesností
4 F float číslo s plovoucí řádovou čárkou s jednoduchou přesností
5 I int 32bitové celé číslo se znaménkem
6 J long 64bitové celé číslo se znaménkem
7 Ljméno_třídy; reference instance třídy zadaného jména
8 S short 16bitové celé číslo se znaménkem
9 Z boolean pravdivostní hodnota true/false
10 V void void – pouze u návratového typu
11 [ reference jednodimenzionální pole

Podívejme se nyní na několik jednoduchých příkladů zápisu signatur:

void foo()           ()V
int foo()            ()I
int foo(int, int)    (II)I
float foo()          ()F
double foo()         ()D
void foo(int)        (I)V
int[] foo()          ()[I
int[][] foo()        ()[[I
String foo()         ()Ljava.lang.String;
void foo(String)     (Ljava/lang/String;)V
String foo(String)   (Ljava/lang/String;)Ljava.lang.String;

2. Alokace a dealokace paměti v agentech využívajících rozhraní JVM TI

Ještě předtím, než si ve čtvrté kapitole ukážeme zdrojový text čtvrtého demonstračního agenta, se musíme dozvědět základní informace o dvou funkcích nazvaných Allocate a Deallocate z rozhraní JVM TI určených pro alokaci a dealokaci paměti přímo za běhu agenta (runtime). Tato dvojice funkcí obecně provádí správu paměti odlišným způsobem než céčkové funkce malloc/calloc/alloca/free a samozřejmě se taktéž liší od dvojice operátorů new/delete používaných v programovacím jazyce C++. Z tohoto důvodu budu v případě demonstračních agentů při alokacích a dealokacích paměti většinou používat funkce nabízené přímo rozhraním JVM TI, což má tu výhodu, že není nutné složitě zjišťovat, jakým způsobem se má provést dealokace (uvolnění) bloku paměti ve funkci, které se „odněkud z neznáma“ předává ukazatel na dříve alokovaný paměťový region. Pojďme si tedy nyní obě zmiňované funkce popsat.

Pro alokaci paměťového regionu (typicky se jedná o řetězce – názvy tříd, metod, atributů atd.) je vhodné namísto funkcí ze standardní céčkové knihovny použít funkci nazvanou Allocate, které se předá požadovaná velikost alokované paměti (zde je použit typ jlong z rozhraní JNI – Java Native Interface, který většinou odpovídá datovému typu long int či long long int) a taktéž ukazatel na řetězec, který je po zavolání funkce Allocate naplněn adresou, na níž začíná oblast vyhrazená pro nově alokovaný řetězec/paměťový blok. Za řetězec považujme v céčku pro jednoduchost typ char*, ukazatel na řetězec má potom typ char**. Je nutné mít na paměti, že návratovou hodnotou této funkce NENÍ ukazatel na vytvořený řetězec/paměťový region (jako je tomu například u funkcí malloc, calloc či alloca ze standardní céčkové knihovny), ale chybový kód, podobně jako je tomu u většiny funkcí z rozhraní JVM TI. Ukazatel na alokovaný paměťový region je nutné vždy získat přes druhý parametr této funkce:

jvmtiError
Allocate(
            jvmtiEnv* env,
            jlong size,
            unsigned char** mem_ptr)

Opakem funkce Allocate je funkce Deallocate, která naopak provede uvolnění dříve naalokovaného paměťového regionu. Této funkci se opět předává odkaz na strukturu jvmtiEnv a ukazatel na oblast paměti alokovanou dříve pomocí funkce Allocate:

jvmtiError
Deallocate(
            jvmtiEnv* env,
            unsigned char* mem)

3. Zjištění jmen a signatur volaných metod

Se znalostí funkce Allocate() a především pak funkce Deallocate() je již možné upravit demonstrační příklad ze závěru předchozího článku takovým způsobem, aby mohl vypisovat jména a signatury všech volaných metod. Pro tyto účely může náš testovací agent použít funkci nazvanou GetMethodName(), jejíž hlavička vypadá následovně:

jvmtiError
GetMethodName(
            jvmtiEnv* env,
            jmethodID method,
            char** name_ptr,
            char** signature_ptr,
            char** generic_ptr)

Prvním parametrem této funkce je odkaz na strukturu jvmtiEnv, který je použit ve většině funkcí rozhraní JVM TI, takže se pro nás nejedná o žádnou novinku. Druhým parametrem funkce je identifikátor metody, který je typu jmethodID. Tento typ je většinou v hlavičkovém souboru jvmti.h deklarován jako long int či long long int v závislosti na architektuře počítače a použitém systému. Další tři parametry jsou již mnohem zajímavější, protože jejich datový typ je char**, tj. jedná se o ukazatele na „řetězce“, kde řetězce jsou v céčku představovány typem char*, popř. const char *. Důvod, proč se do funkce GetMethodName předávají ukazatele na řetězce, je prostý – tato funkce totiž sama provede alokaci těchto řetězců, které budou mít vždy vhodnou délku, nezávisle na tom, jak dlouhé je jméno metody a jaký je počet a typ jejich parametrů (to samozřejmě ovlivňuje délku signatury). Vrácené řetězce jsou v UTF-8, což by však neměl být pro moderní systémy (a jejich konzole) žádný problém :-). Pouze je zapotřebí s tímto kódováním počítat v těch případech, kdy by se provádělo další zpracování řetězců (regulární výrazy atd.).

Oproti mnoha funkcím ze standardní knihovny céčka tak nemusíme složitě přemýšlet, jak dlouhé řetězce je zapotřebí alokovat, pamatovat si maximální délku řetězců atd. Musíme však dodržet jednu podmínku – řetězce vrácené funkcí GetMethodName je nutné (ideálně co nejdříve) dealokovat zavoláním funkce Deallocate(), nelze tedy použít funkci free() ze standardní céčkové knihovny či delete v C++. Pokud se do třetího až pátého parametru funkce GetMethodName předá hodnota NULL, nebude se příslušný řetězec alokovat, což využijeme u posledního parametru, jenž nás (prozatím) nezajímá.

S využitím funkcí GetMethodName a Deallocate můžeme napsat upravenou variantu callback funkce callback_on_method_entry:

/*
 * Callback funkce zavolana pri zavolani metody.
 */
static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env,
        jthread thread, jmethodID method)
{
    char *name_ptr;
    char *signature_ptr;
 
    /* MSG("Got Method Entry event"); */
    (*jvmti)->GetMethodName(jvmti, method, &name_ptr, &signature_ptr, NULL);
    printf("called method %s with signature %s\n", name_ptr, signature_ptr);
    (*jvmti)->Deallocate(jvmti, (unsigned char *)name_ptr);
    (*jvmti)->Deallocate(jvmti, (unsigned char *)signature_ptr);
}

Tato funkce bude využita ve čtvrtém demonstračním příkladu, jehož zdrojový kód je popsán v následující kapitole.

4. Čtvrtý demonstrační příklad: výpis signatur všech volaných metod

S využitím informací získaných v předchozích třech kapitolách je již poměrně jednoduché upravit demonstrační příklad ukázaný v závěru předchozího článku takovým způsobem, aby vypisoval jména a současně i signatury volaných metod. Připomeňme si, že pro implementaci této funkcionality agenta je zapotřebí provést čtyři kroky:

  1. Nastavit požadovanou speciální schopnost agenta pomocícapabilities.can_ge­nerate_method_entry_events = 1; ve funkci set_capabilities() a otestovat, zda daný virtuální stroj tuto schopnost nabízí.
  2. Vytvořit callback funkci callback_on_method_entry (jméno se samozřejmě může lišit, typy parametrů však nikoli) s hlavičkou static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env, jthread thread, jmethodID method).
  3. Zaregistrovat tuto callback funkci pomocí callbacks.MethodEntry = &callback_on_method_entry;, což se v našich demonstračních agentech provádí ve funkci register_all_callback_functions().
  4. Nastavit zachytávání tohoto typu události s využitím funkceSetEventNotificationMode() volané v našich demonstračních agentech z funkce set_event_notification_mode.

Následuje výpis celého zdrojového kódu čtvrtého demonstračního JVM TI agenta:

/*
 * JVM TI agent, ktery dokaze registrovat vstup do metody
 * a nasledne vypsat i jeji signaturu.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent4:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* jedna specialni schopnost agenta */
    capabilities.can_generate_method_entry_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Callback funkce zavolana pri inicializaci virtualniho stroje.
 */
static void JNICALL callback_on_vm_init(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thread)
{
    MSG("Got VM init event");
}
 
/*
 * Callback funkce zavolana pri ukonceni cinnosti virtualniho stroje.
 */
static void JNICALL callback_on_vm_death(jvmtiEnv *jvmti_env, JNIEnv* env)
{
    MSG("Got VM Death event");
}
 
/*
 * Callback funkce zavolana pri zavolani metody.
 */
static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env,
        jthread thread, jmethodID method)
{
    char *name_ptr;
    char *signature_ptr;
 
    /* MSG("Got Method Entry event"); */
    (*jvmti)->GetMethodName(jvmti, method, &name_ptr, &signature_ptr, NULL);
    printf("called method %s with signature %s\n", name_ptr, signature_ptr);
    (*jvmti)->Deallocate(jvmti, (unsigned char *)name_ptr);
    (*jvmti)->Deallocate(jvmti, (unsigned char *)signature_ptr);
}
 
/*
 * Registrace vsech callback funkci.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_VM_INIT */
    callbacks.VMInit = &callback_on_vm_init;
 
    /* JVMTI_EVENT_VM_DEATH */
    callbacks.VMDeath = &callback_on_vm_death;
 
    /* JVMTI_EVENT_METHOD_ENTRY */
    callbacks.MethodEntry = &callback_on_method_entry;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* Potrebujeme zachytavat udalost inicializace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_INIT)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost ukonceni prace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_DEATH)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost pri vstupu do metody. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_METHOD_ENTRY)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech (dvou) callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}

Funkcionalitu tohoto agenta si můžeme snadno odzkoušet například na následujícím testu:

public class Test4 {
 
    public static void method1(String s1, String s2) {
        method2(s1 + " " + s2);
    }
 
    public static void method2(String s) {
        method3(s);
    }
 
    public static void method3(String s) {
        System.err.println(s);
    }
 
    public static void main(String[] args) {
        method1("Hello", "world");
        method1("Hello", "world");
    }
}

Způsob překladu i spuštění je shodný se způsobem popsaným v předchozí části tohoto seriálu, liší se jen název agenta i název testovacího javovského programu:

# překlad agenta
gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent4.so agent4.c
 
# překlad testu
javac Test4.java
 
# spuštění JVM s agentem a testem
java -agentpath:./libagent4.so Test4

Na konzoli by se měl vypsat následující text:

Agent4: Agent_OnLoad
Agent4: JVM TI version is correct
Agent4: Got VM init event
...
...
...
called method method2 with signature (Ljava/lang/String;)V
called method method3 with signature (Ljava/lang/String;)V
called method println with signature (Ljava/lang/String;)V
...
...
...
Agent4: Got VM Death event
Agent4: Agent_OnUnload

Počet skutečně volaných metod se liší v závislosti na použitém virtuálním stroji Javy, ale prakticky vždy půjde o několik set až několik tisíc (!) metod, což je i jeden z důvodů, proč je start JVM poměrně pomalý.

5. Použití zámků v agentech využívajících rozhraní JVM TI

V některých případech je nutné zajistit, aby se nějaká callback funkce v agentovi prováděla v daný okamžik pouze v jednom vláknu. U některých typů callback funkcí to zajišťuje samotný virtuální stroj Javy, u dalších callback funkcí je zajištění synchronizace plně v rukou vývojáře, který vytváří agenta. Ve skutečnosti není zajištění synchronizace nic složitého, protože nám příslušné prostředky nabízí samotné rozhraní JVM TI. V něm lze totiž využít zámek představovaný (z hlediska agenta) objektem typu jrawMonitorID. Těchto zámků lze vytvořit prakticky libovolné množství, ovšem pokud se v callback funkcích současně použije více zámků, musí si programátor pohlídat, aby nedošlo k dead locku. Aby jsme tomu zabránili, bude se v demonstračních agentech používat pouze jeden zámek, což sice nemusí být optimální z hlediska celkového výkonu JVM, ale je to bezpečnější postup. Pro práci se zámky se používá několik funkcí nazvaných CreateRawMonitor, DestroyRawMonitor, RawMonitorEnter, RawMonitorExit, RawMonitorWait atd. My si prozatím vystačíme jen se třemi funkcemi popsanými v dalších odstavcích.

6. Funkce CreateRawMonitor, RawMonitorEnter a RawMonitorExit

V demonstračním agentovi budeme používat jeden globální zámek, který bude reprezentován objektem typu jrawMonitorID:

jrawMonitorID  global_lock;

Pro vytvoření zámku se používá funkce CreateRawMonitor, které se předá textový identifikátor zámku a taktéž ukazatel na naši globální proměnnou. Funkce CreateRawMonitor samozřejmě vrací stavový kód, který je vhodné otestovat:

/*
 * Vytvoreni zamku.
 */
jvmtiError create_raw_monitor(jvmtiEnv *jvmti)
{
    jvmtiError error_code;

    error_code = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot create raw monitor");

    return error_code;
}

Pro vstup do kritické sekce (hlídané monitorem/zámkem) se používá funkce nazvaná RawMonitorEnter. Tato funkce zajistí, že do sekce může vstoupit pouze jedno vlákno, ostatní vlákna budou čekat, až aktuální vlákno sekci opustí:

/*
 * Vstup do kriticke sekce.
 */
static void enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;

    error_code = (*jvmti)->RawMonitorEnter(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot enter with raw monitor");
}

Výstup z kritické sekce hlídané monitorem/zámkem se oznamuje zavoláním funkce RawMonitorExit:

/*
 * Vystup z kriticke sekce
 */
static void exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;

    error_code = (*jvmti)->RawMonitorExit(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot exit with raw monitor");
}

Pro větší čitelnost zdrojového kódu agenta je vhodné celou kritickou sekci omezenou voláním našich funkcí enter_critical_section() a exit_critical_section() uzavřít do samostatného bloku. Programový kód zapsaný v kritické sekci by měl být co nejkratší (z hlediska doby provedení) a v žádném případě by se neměl výstup z kritické sekce přeskakovat pomocí lokálních (goto) či globálních (longjmp) skoků:

/*
 * Callback funkce zavolana pri zavolani metody.
 */
static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env,
        jthread thread, jmethodID method)
{
    char *name_ptr;
    char *signature_ptr;

    enter_critical_section(jvmti);
    {
        /* MSG("Got Method Entry event"); */
        (*jvmti)->GetMethodName(jvmti, method, &name_ptr, &signature_ptr, NULL);
        printf("called method %s with signature %s\n", name_ptr, signature_ptr);
        (*jvmti)->Deallocate(jvmti, (unsigned char *)name_ptr);
        (*jvmti)->Deallocate(jvmti, (unsigned char *)signature_ptr);
    }
    exit_critical_section(jvmti);
}

7. Pátý demonstrační příklad: agent pro výpis volaných metod využívající zámky

Pátý demonstrační agent se od čtvrtého agenta liší pouze v jedné maličkosti – využívá se zde globální zámek podle postupu popsaného v předchozí kapitole. Žádné další odlišnosti od předchozího příkladu zde nenalezneme:

/*
 * JVM TI agent, ktery dokaze registrovat vstup do metody
 * a nasledne vypsat i jeji signaturu.
 *
 * Zdrojovy kod byl doplnen o pouziti zamku.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent5:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Vytvoreni zamku.
 */
jvmtiError create_raw_monitor(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot create raw monitor");
 
    return error_code;
}
 
/*
 * Vstup do kriticke sekce.
 */
static void enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorEnter(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot enter with raw monitor");
}
 
/*
 * Vystup z kriticke sekce
 */
static void exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorExit(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot exit with raw monitor");
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* jedna specialni schopnost agenta */
    capabilities.can_generate_method_entry_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Callback funkce zavolana pri inicializaci virtualniho stroje.
 */
static void JNICALL callback_on_vm_init(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thread)
{
    MSG("Got VM init event");
}
 
/*
 * Callback funkce zavolana pri ukonceni cinnosti virtualniho stroje.
 */
static void JNICALL callback_on_vm_death(jvmtiEnv *jvmti_env, JNIEnv* env)
{
    MSG("Got VM Death event");
}
 
/*
 * Callback funkce zavolana pri zavolani metody.
 */
static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env,
        jthread thread, jmethodID method)
{
    char *name_ptr;
    char *signature_ptr;
 
    enter_critical_section(jvmti);
    {
        /* MSG("Got Method Entry event"); */
        (*jvmti)->GetMethodName(jvmti, method, &name_ptr, &signature_ptr, NULL);
        printf("called method %s with signature %s\n", name_ptr, signature_ptr);
        (*jvmti)->Deallocate(jvmti, (unsigned char *)name_ptr);
        (*jvmti)->Deallocate(jvmti, (unsigned char *)signature_ptr);
    }
    exit_critical_section(jvmti);
}
 
/*
 * Registrace vsech callback funkci.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_VM_INIT */
    callbacks.VMInit = &callback_on_vm_init;
 
    /* JVMTI_EVENT_VM_DEATH */
    callbacks.VMDeath = &callback_on_vm_death;
 
    /* JVMTI_EVENT_METHOD_ENTRY */
    callbacks.MethodEntry = &callback_on_method_entry;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* Potrebujeme zachytavat udalost inicializace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_INIT)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost ukonceni prace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_DEATH)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost pri vstupu do metody. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_METHOD_ENTRY)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech (dvou) callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* vytvoreni zamku */
    if ((error_code = create_raw_monitor(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}

8. Šestý demonstrační příklad: agent registrující práci garbage collectoru

Dnes si ukážeme ještě jeden poměrně praktický příklad – agenta, který dokáže registrovat začátek a konec činnosti správce paměti (garbage collectoru). Tento agent sice nemusí správně fungovat se všemi správci paměti, ovšem u těch základních (Mark and Sweep atd.) je jeho funkce zaručena. Činnost agenta zajišťuje dvojice callback funkcí zavolaných při startu GC a ukončení GC. Ve chvíli volání těchto callback funkcí je činnost celého virtuálního stroje pozastavena, i přesto je však lepší tělo těchto dvou callback funkcí uzavřít do kritické sekce.

V šestém demonstračním agentovi jsou použity dvě nové globální proměnné nazvané gc_start a gc_total, které slouží ke sledování aktuální i celkové doby běhu správce paměti. Proměnná gc_start je nastavována v callback funkci callback_on_gc_start na aktuální hodnotu (počet tiků interních systémových hodin) vrácenou funkcí clock(). Tímto způsobem zaznamenaná hodnota je použita ve druhé callback funkci callback_on_gc_finish pro výpočet doby běhu správce paměti i pro výpočet celkové doby aktivního běhu vlákna (vláken) se správcem paměti:

/*
 * JVM TI agent, ktery dokaze registrovat praci GC.
 */
 
#include <stdlib.h>
#include <string.h>
#include <time.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent6:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
/* Zacatek behu GC */
clock_t gc_start;
 
/* Celkovy cas behu GC */
clock_t gc_total = 0;
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Vytvoreni zamku.
 */
jvmtiError create_raw_monitor(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot create raw monitor");
 
    return error_code;
}
 
/*
 * Vstup do kriticke sekce.
 */
static void enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorEnter(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot enter with raw monitor");
}
 
/*
 * Vystup z kriticke sekce
 */
static void exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorExit(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot exit with raw monitor");
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* vyuzivame jednu specialni schopnost agenta */
    capabilities.can_generate_garbage_collection_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Callback funkce zavolana pri inicializaci virtualniho stroje.
 */
static void JNICALL callback_on_vm_init(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thread)
{
    MSG("Got VM init event");
}
 
/*
 * Callback funkce zavolana pri ukonceni cinnosti virtualniho stroje.
 */
static void JNICALL callback_on_vm_death(jvmtiEnv *jvmti_env, JNIEnv* env)
{
    MSG("Got VM Death event");
}
 
/*
 * Zacatek behu GC.
 */
static void JNICALL callback_on_gc_start(jvmtiEnv *jvmti_env)
{
    enter_critical_section(jvmti_env);
    {
        gc_start = clock();
        printf("gc start\n");
    }
    exit_critical_section(jvmti_env);
}
 
/*
 * Konec behu GC.
 */
static void JNICALL callback_on_gc_finish(jvmtiEnv *jvmti_env)
{
    enter_critical_section(jvmti_env);
    {
        clock_t gc_end = clock();
        clock_t diff = gc_end - gc_start;
        gc_total += diff;
        printf("gc end: %f sec\n", (double)diff/CLOCKS_PER_SEC);
    }
    exit_critical_section(jvmti_env);
}
 
/*
 * Registrace vsech callback funkci.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_VM_INIT */
    callbacks.VMInit = &callback_on_vm_init;
 
    /* JVMTI_EVENT_VM_DEATH */
    callbacks.VMDeath = &callback_on_vm_death;
 
    /* JVMTI_EVENT_GARBAGE_COLLECTION_START */
    callbacks.GarbageCollectionStart  = &callback_on_gc_start;
 
    /* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
    callbacks.GarbageCollectionFinish = &callback_on_gc_finish;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* Potrebujeme zachytavat udalost inicializace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_INIT)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost ukonceni prace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_DEATH)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost pri zacatku behu GC. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_GARBAGE_COLLECTION_START)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost pri ukonceni behu GC. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* vytvoreni zamku */
    if ((error_code = create_raw_monitor(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
    printf("Total GC time: %f sec\n", (double)gc_total/CLOCKS_PER_SEC);
}

Funkci agenta otestujeme na velmi jednoduchém javovském programu, který i přes svoji jednoduchost správce paměti docela hodně „potrápí“, protože každé použití operátoru + nad řetězcem vede k vytvoření nového objektu:

public class Test6 {
 
    public static void main(String[] args) {
        String s = "";
        for (int i=0; i < 10000; i++)
        {
            s = s + " " + i;
        }
    }
 
}

Skript pro překlad agenta a spuštění JVM s agentem:

widgety

# překlad agenta
gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent6.so agent6.c
 
# překlad testu
javac Test6.java
 
# spuštění JVM s agentem
java -agentpath:./libagent6.so Test6

Po spuštění JVM s agentem a testem by se na konzoli měly vypsat zprávy podobající se následujícímu výpisu:

Agent6: Agent_OnLoad
Agent6: JVM TI version is correct
Agent6: Got VM init event
gc start
gc end: 0.010000 sec
...
...
...
gc start
gc end: 0.000000 sec
gc start
gc end: 0.000000 sec
gc start
gc end: 0.000000 sec
gc start
gc end: 0.000000 sec
...
...
...
Agent6: Got VM Death event
Agent6: Agent_OnUnload
Total GC time: 0.220000 sec

Z tohoto výpisu je patrné, že správce paměti je spouštěn (nad young generation) s poměrně velkou frekvencí takovým způsobem, aby nemuselo docházet k pozastavení běhu vláken aplikace po delší dobu, což by se mohlo negativně promítnout do doby odezvy aplikace.

9. Odkazy na Internetu

  1. The JVM Tool Interface (JVM TI): How VM Agents Work
    http://www.oracle.com/technet­work/articles/javase/jvm-ti-141370.html
  2. JVM Tool Interface Version 1.2
    http://docs.oracle.com/ja­vase/7/docs/platform/jvmti/jvmti­.html
  3. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  4. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  5. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  6. ClojureScript One: Index
    http://clojurescriptone.com/in­dex.html
  7. ClojureScript One: Documentation
    http://clojurescriptone.com/do­cumentation.html
  8. ClojureScript One: Wiki
    https://github.com/brento­nashworth/one/wiki
  9. ClojureScript: Quick Start
    https://github.com/clojure/clo­jurescript/wiki/Quick-Start
  10. Getting Started with ClojureScript (and FW/1)
    http://corfield.org/entry/getting-started-with-clojurescript-and-fw-1
  11. First ClojureScript experiences: using Raphaël
    http://maurits.wordpress.com/2012/02/13/fir­st-clojurescript-experiences-using-raphael/
  12. Raphaël-JavaScript Library
    http://raphaeljs.com/
  13. A detailed installation Guide for VimClojure 2.2
    http://www.duenas.at/new_ho­mepage/vimclojure
  14. VimClojure : A filetype, syntax and indent plugin for Clojure
    http://www.vim.org/scripts/scrip­t.php?script_id=2501
  15. Nailgun server
    http://www.martiansoftware­.com/nailgun/background.html
  16. SLIME (Wikipedia)
    http://en.wikipedia.org/wiki/SLIME
  17. slime.vim
    http://s3.amazonaws.com/mps/slime.vim
  18. Textový editor Vim jako IDE: 1. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide/
  19. Textový editor Vim jako IDE: 2. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-2-cast/
  20. Textový editor Vim jako IDE: 3. část (omni completion)
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-3-cast/
  21. Textový editor Vim jako IDE: 4. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-4-cast/
  22. Textový editor Vim jako IDE: 5. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-5-cast/
  23. Textový editor Vim jako IDE: 6. část – Vim Script
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-6-cast-vim-script/
  24. Textový editor Vim jako IDE: 7. část – Vim Script
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-7-cast-vim-script/
  25. Textový editor Vim jako IDE: 8. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-8-cast/
  26. Textový editor Vim jako IDE: 9. část – pluginy Netrw a snipMate
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-9-cast-pluginy-netrw-a-snipmate/
  27. Textový editor Vim jako IDE: 10. část – různé tipy a triky
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-10-cast-ruzne-tipy-a-triky/
  28. Přenos textů mezi Vimem a dalšími aplikacemi
    http://www.root.cz/clanky/prenos-textu-mezi-vimem-a-dalsimi-aplikacemi/
  29. Textový editor Vim: konfigurace pravítka a stavového řádku
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-12-cast-konfigurace-pravitka-a-stavoveho-radku/
  30. Textový editor Vim: automatické formátování textů
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-14-cast-automaticke-formatovani-textu/
  31. Textový editor Vim: automatické formátování textů
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-automaticke-formatovani-textu-dokonceni/
  32. Textový editor Vim: editace XML a HTML
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-15-cast-editace-xml-a-html/
  33. Textový editor Vim: kooperace mezi Vimem a skripty
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-16-cast-kooperace-mezi-vimem-a-skriptovacimi-jazyky/
  34. Textový editor Vim: kooperace mezi Vimem a jazykem Perl
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-17-cast-kooperace-mezi-vimem-a-jazykem-perl/
  35. Textový editor Vim: konfigurace a překlad Vimu
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-18-cast-konfigurace-a-preklad-vimu/
  36. Counterclockwise
    http://code.google.com/p/cou­nterclockwise/
  37. Clojure IDEs – The Grand Tour
    http://www.bestinclass.dk/in­dex.clj/2010/03/clojure-ides-the-grand-tour-getting-started.html
  38. Light Table – a new IDE concept
    http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/
  39. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  40. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  41. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  42. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  43. Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-i-getting.html
  44. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  45. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  46. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  47. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  48. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  49. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  50. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  51. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  52. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  53. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  54. Clojure.org: Clojure home page
    http://clojure.org/downloads
  55. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  56. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  57. Clojure.org: Atoms
    http://clojure.org/Atoms
  58. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  59. A Couple of Clojure Agent Examples
    http://lethain.com/a-couple-of-clojure-agent-examples/
  60. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  61. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  62. 4Clojure
    http://www.4clojure.com/
  63. ClojureDoc
    http://clojuredocs.org/
  64. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  65. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  66. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  67. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  68. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  69. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  70. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  71. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  72. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  73. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  74. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  75. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  76. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  77. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  78. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  79. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  80. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  81. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  82. 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/
  83. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  84. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  85. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  86. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  87. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  88. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  89. BCEL Home page
    http://commons.apache.org/bcel/
  90. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  91. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  92. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  93. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  94. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  95. ASM Home page
    http://asm.ow2.org/
  96. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  97. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  98. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  99. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  100. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  101. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  102. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  103. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  104. Cobertura
    http://cobertura.sourceforge.net/
  105. FindBugs
    http://findbugs.sourceforge.net/
  106. GNU Classpath
    www.gnu.org/s/classpath/
  107. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  108. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  109. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  110. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  111. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  112. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  113. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  114. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  115. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  116. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  117. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  118. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  119. Scala Programming Language
    http://www.scala-lang.org/
  120. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  121. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  122. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  123. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  124. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  125. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  126. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  127. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  128. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  129. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  130. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  131. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  132. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  133. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  134. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  135. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  136. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html
Našli jste v článku chybu?
DigiZone.cz: Nova opět stahuje „milionáře“

Nova opět stahuje „milionáře“

DigiZone.cz: DVB-T2 ověřeno: ČRa doplňují seznam

DVB-T2 ověřeno: ČRa doplňují seznam

DigiZone.cz: Sony MP-CL1A: miniaturní projektor

Sony MP-CL1A: miniaturní projektor

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

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

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

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

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

dTest odhalil ten nejlepší kečup

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

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

Lupa.cz: Blíží se konec Wi-Fi sítí bez hesla?

Blíží se konec Wi-Fi sítí bez hesla?

Podnikatel.cz: Znáte už 5 novinek k #EET

Znáte už 5 novinek k #EET

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

Vitalia.cz: Tahák, jak vyzrát nad zápachem z úst

Tahák, jak vyzrát nad zápachem z úst

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

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

Wimbledon na Nova Sport až do 2019

Podnikatel.cz: EET pro e-shopy? Postavené na hlavu

EET pro e-shopy? Postavené na hlavu

Lupa.cz: Hackeři mají data z půlmiliardy účtů Yahoo

Hackeři mají data z půlmiliardy účtů Yahoo