Pohled pod kapotu JVM - práce se zásobníkem a haldou přes rozhraní JVM TI

Pavel Tišnovský 11. 12. 2012

V dnešní části seriálu o programovacím jazyce Java i o virtuálním stroji Javy si řekneme, jak je možné přes rozhraní JVM TI získávat informace ze zásobníkových rámců (stack frames) i z haldy (heap). Jedná se o informace, které jsou mnohdy velmi důležité zejména při ladění či při profilování javovských aplikací.

Obsah

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (6.část: práce se zásobníkem a haldou)

2. Získání informací o historii volaných metod při detekci vzniku výjimky

3. Funkce print_stack_trace() a úprava callback funkce callback_on_exception()

4. Šestnáctý demonstrační příklad – výpis volaných metod při detekci vzniku výjimky

5. Přečtení čísla řádků ke všem metodám na zásobníku a další vylepšení agenta

6. Sedmnáctý demonstrační příklad – výpis volaných metod včetně čísel řádků

7. Analýza obsahu haldy (heapu)

8. Zdrojové kódy demonstračních příkladů

9. Odkazy na Internetu

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (6.část: práce se zásobníkem a haldou)

V předchozí části seriálu o programovacím jazyce Java i o vlastnostech virtuálního stroje Javy jsme si na příkladu třech demonstračních JVM TI agentů ukázali způsob detekce vzniku výjimky v javovských aplikacích, a to i v těch případech, kdy se vývojáři budou snažit výjimky před okolním světem skrýt, například jejich zachycením v prázdném bloku catch (pravděpodobně není zapotřebí čtenářům tohoto článku připomínat, že se v naprosté většině případů jedná o velmi špatný programátorský zvyk; minimálně je vhodné vypsat základní informace o výjimce společně se stack trace). Víme již, že vhodnou konfigurací JVM TI agenta lze docílit toho, aby se při vzniku jakékoli výjimky, ať již výjimky zachycované či nezachycované, zavolala callback funkce s následující hlavičkou:

/*
 * Callback funkce zavolaná ve chvíli vzniku výjimky,
 * resp. detekce výjimky virtuálním strojem Javy.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv* env,
            jthread thread,
            jmethodID method,
            jlocation location,
            jobject exception,
            jmethodID catch_method,
            jlocation catch_location);

Ještě si zopakujme význam všech osmi parametrů této callback funkce:

# Parametr Typ Význam
1 jvmti_env jvmtiEnv* JVM TI prostředí agenta (je předáváno do většiny callback funkcí)
2 jni_env JNIEnv* JNI prostředí platné pro dané vlákno (je předáváno do některých callback funkcí)
3 thread jthread vlákno, v němž dojde k vyhození (vzniku) výjimky
4 method jmethodID metoda, ve které výjimka vznikla
5 location jlocation umístění kódu, v němž výjimka vznikla
6 exception jobject identifikátor objektu, který nese informaci o výjimce
7 catch_method jmethodID metoda, ve které se výjimka zachycuje
8 catch_location jlocation umístění kódu, který výjimku zachycuje (odpovídá indexu instrukce)

Dnes si řekneme, jakým způsobem je možné využít parametr jthread thread, který jsme prozatím tiše ignorovali. V tomto parametru je předán identifikátor (javovského) objektu reprezentujícího vlákno, v němž k výjimce došlo a lze ho využít zejména pro získání informací o volaných metodách – programátoři označují zpětný seznam volaných metod názvem stack trace. Dále si dnes řekneme základní informace o tom, jakým způsobem lze přečíst obsah haldy (heapu), tj. informací o objektech, které jsou zde uloženy.

2. Získání informací o historii volaných metod při detekci vzniku výjimky

Při tvorbě dnešního prvního demonstračního příkladu navážeme na zdrojový kód patnáctého demonstračního JVM TI agenta. Tento zdrojový kód lze získat z Mercurial repositáře dostupného na adrese http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/ (viz též předchozí část tohoto seriálu). Další informací, kterou bude nově vyvíjený testovací JVM TI agent tisknout, je výpis seznamu volaných metod získaný ze zásobníkových rámců. Připomeňme si, jakým způsobem interně pracuje virtuální stroj Javy při spuštění aplikace – pro každé vlákno aplikace je vytvořen samostatný zásobník (stack) a pokud se v nějakém vláknu volá metoda, je pro toto volání na zásobníku vytvořen takzvaný zásobníkový rámec (stack frame), v němž je uložena jak informace o bodu návratu z metody (kam povede instrukce return), tak i hodnoty všech parametrů předaných metodě i oblast s lokálními proměnnými metody. Nás bude v tento okamžik zajímat především informace o návratových bodech, tj. o volaných metodách. K těmto informacím se můžeme (pro dané vlákno) dostat velmi snadno s využitím funkce nazvané (nikoli překvapivě) GetStackTrace() s následující hlavičkou:

/* Získání seznamu volaných metod. */
jvmtiError GetStackTrace(
            jvmtiEnv* env,
            jthread   thread,
            jint      start_depth,
            jint      max_frame_count,
            jvmtiFrameInfo* frame_buffer,
            jint*     count_ptr)

Význam prvních dvou parametrů je zřejmý – nejprve se přes ukazatel env předává struktura s rozhraním JVM TI, ve druhém parametru pak identifikátor vlákna, pro nějž chceme získat výpis volaných metod (typ jthread, jenž je odvozen od jobject). Ve třetím a čtvrtém parametru je specifikováno, o jakých metodách potřebujeme informace získat – tj. počet metod i index první volané metody. Připomeňme si, že na zásobníku jsou informace uloženy formou LIFO, tj. poslední volaná metoda je vrácena jako metoda první, přesněji řečeno metoda s indexem 0. Funkce GetStackTrace() po svém zavolání vytvoří tabulku s informacemi o volaných metodách a ukazatel na tuto tabulku je vrácen přes pátý parametr této funkce. Navíc se v parametru šestém vrátí skutečná velikost tabulky, tj. počet informací o volaných metodách. Tento údaj se může lišit od číselné hodnoty předávané ve třetím a čtvrtém parametru, a to z toho důvodu, že zásobník může ve skutečnosti obsahovat menší množství údajů o volaných metodách, než je požadováno.

V případě potřeby lze celkový počet volaných metod pro dané vlákno zjistit s využitím funkce GetFrameCount():

/* Celkový počet volaných metod pro dané vlákno. */
jvmtiError GetFrameCount(
            jvmtiEnv* env,
            jthread thread,
            jint* count_ptr)

Tabulka vrácená funkcí GetStackTrace() obsahuje pole struktur, přičemž každá struktura je složena z dvojice identifikátor metody + lokace. Tento druhý údaj lze v případě potřeby snadno převést na číslo řádku, což si ukážeme v sedmnáctém demonstračním příkladu popsaném v kapitole číslo 5 a taktéž kapitole 6:

typedef struct {
    jmethodID method;
    jlocation location;
} jvmtiFrameInfo;

3. Funkce print_stack_trace() a úprava callback funkce callback_on_exception()

Podívejme se nyní na kód funkce print_stack_trace(), kterou lze použít pro výpis maximálně deseti metod volaných ve vláknu, jehož identifikátor je do funkce předán. Povšimněte si především toho, že funkce GetStackTrace() ve skutečnosti nealokuje pole/tabulku s informacemi o volaných funkcích. Alokaci musíme udělat sami a taktéž se sami postarat o uvolnění tohoto pole. Pokud je pole skutečně naplněno, dojde v programové smyčce ke zjištění jména třídy a jména metody pro každou volanou metodu a ihned poté je tato informace vypsána na standardní výstup. Musíme se taktéž postarat o uvolnění řetězců s využitím funkce Deallocate(), tuto problematiku však již dobře známe z předchozích částí tohoto seriálu:

/*
 * Vypis obsahu zasobniku.
 */
void print_stack_trace(
            jvmtiEnv *jvmti_env,
            jthread   thr)
{
#define MAX_STACK_TRACE_DEPTH 10
    jvmtiFrameInfo stack_frames[MAX_STACK_TRACE_DEPTH];
    jclass declaring_class;
    int count;
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    int i;
 
    /* Ziskat trasovaci inforamce */
    (*jvmti_env)->GetStackTrace(jvmti_env, thr, 0, MAX_STACK_TRACE_DEPTH, stack_frames, &count);
    if (count == 1)
    {
        printf("No stack trace!\n");
    }
    printf("Exception Stack Trace\n");
    printf("Stack Trace Depth: %d\n", count);
    for (i = 0; i < count; i++) {
        jvmtiFrameInfo stack_frame = stack_frames[i];
 
        /* ziskat jmeno methody i jeji tridu */
        (*jvmti_env)->GetMethodName(jvmti_env, stack_frame.method, &method_name_ptr, &method_signature_ptr, NULL);
        (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, stack_frame.method, &declaring_class);
        (*jvmti_env)->GetClassSignature(jvmti_env, declaring_class, &class_name_ptr, NULL);
 
        /* upravit jmeno tridy */
        updated_class_name_ptr = update_class_name(class_name_ptr, '.');
 
        /* provest vlastni vypis */
        printf("\tat %s%s() with signature %s\n", updated_class_name_ptr, method_name_ptr, method_signature_ptr);
 
        /* dealokace vsech ziskanych pametovych struktur */
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_name_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_signature_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)class_name_ptr);
    }
}

Volání funkce print_stack_trace() jednoduše přidáme do callback funkce callback_on_exception(), což je ukázáno v následujícím výpisu. Povšimněte si, že můžeme velmi snadno zajistit, aby se výpis zásobníku provedl pouze ve chvíli, kdy byl virtuálním strojem Javy detekován vznik nezachycované výjimky. Pokud podmínku okolo volání funkce print_stack_trace() odstraníte, bude se obsah zásobníku vypisovat pro zachycované i nezachycované výjimky:

/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
    int line_number;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) i jmeno tridy */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* ziskat cislo radku */
    line_number = get_line_number(jvmti_env, method, location);
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s() at ",
            updated_class_name_ptr,
            method_name_ptr);
    if (line_number == -1)
    {
        printf("unknown location\n");
    }
    else
    {
        printf("line %d\n", line_number);
    }
 
    if (catch_method == NULL)
    {
        print_stack_trace(jvmti_env, thr);
    }
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}

4. Šestnáctý demonstrační příklad – výpis volaných metod při detekci vzniku výjimky

Nyní již máme dostatek informací pro implementaci šestnáctého demonstračního JVM TI agenta, jehož zdrojový kód je vypsán pod tímto odstavcem:

/*
 * Demonstracni agent, ktery dokaze zaregistrovat vznik
 * zachycovane i nezachycovane vyjimky.
 *
 * V pripade nezachycene vyjimky vypise i stack trace,
 * ktery se podoba vypisu vytvarenemu samotnou JVM,
 * presneji receno metodou Exception.printStackTrace().
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu k tomuto souboru
 * pres volbu -Icesta_k_jvm
 */
#include <jvmti.h>
 
/*
 * Jmeno agenta pouzite ve zpravach vypisovanych
 * na standardni vystup.
 */
#define AGENT_NAME "Agent16:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
 
/*
 * Vypis kodu chyby a chybove zpravy na standardni vystup.
 */
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 chybove hlaseni na standardni vystup.
 */
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 dve specialni schopnosti agenta */
    capabilities.can_generate_exception_events = 1;
    capabilities.can_get_line_numbers = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Uprava jmena tridy pro tisk.
 */
char* update_class_name(char *class_name_ptr, char replace_to)
{
    char *class_name_ptr_;
    char *c;
    if (class_name_ptr != NULL)
    {
        /* odstraneni pocatecniho L na zacatku jmena tridy */
        class_name_ptr_ = class_name_ptr;
        if (class_name_ptr_[0] == 'L')
        {
            class_name_ptr_++;
        }
        /* nahrada znaku ; za tecku ci jiny definovany znak */
        char *last_char = class_name_ptr_ + strlen(class_name_ptr_) - 1;
        if (*last_char == ';')
        {
            *last_char = replace_to;
        }
        /* nahrada vsech znaku '/' za '.' */
        c = class_name_ptr_;
        for (c = class_name_ptr_; *c != 0; c++)
        {
            if (*c == '/') *c = '.';
        }
    }
    return class_name_ptr_;
}
 
/*
 * Ziskani cisla radku pro zadanou metodu a index instrukce.
 */
int get_line_number(jvmtiEnv *jvmti_env, jmethodID method, jlocation location)
{
    int count;
    int line_number = 0;
    int i;
    jvmtiLineNumberEntry *location_table;
    jvmtiError error_code;
 
    if (method == NULL)
    {
        return -1;
    }
 
    /* nacteni tabulky s cisly radku a indexy instrukci */
    error_code = (*jvmti_env)->GetLineNumberTable(jvmti_env, method, &count, &location_table);
    /* v nekterych pripadech se nacist tabulku nepodari */
    if (error_code != JVMTI_ERROR_NONE)
    {
        return -1;
    }
 
    /* projit celou tabulkou */
    for (i = 0; i < count - 1; i++)
    {
        jvmtiLineNumberEntry entry1 = location_table[i];
        jvmtiLineNumberEntry entry2 = location_table[i+1];
        /* pokud se lokace nachazi mezi entry1 (vcetne) a entry2 (krome)
         * nasli jsme spravny radek */
        if (location >= entry1.start_location && location < entry2.start_location)
        {
            line_number = entry1.line_number;
            break;
        }
    }
    /* take se muze jednat o uplne posledni instrukci v metode */
    if (location >= location_table[count-1].start_location)
    {
        line_number = location_table[count-1].line_number;
    }
 
    /* dealokace tabulky */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)location_table);
    return line_number;
}
 
/*
 * Vypis obsahu zasobniku.
 */
void print_stack_trace(
            jvmtiEnv *jvmti_env,
            jthread   thr)
{
#define MAX_STACK_TRACE_DEPTH 10
    jvmtiFrameInfo stack_frames[MAX_STACK_TRACE_DEPTH];
    jclass declaring_class;
    int count;
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    int i;
 
    /* Ziskat trasovaci inforamce */
    (*jvmti_env)->GetStackTrace(jvmti_env, thr, 0, MAX_STACK_TRACE_DEPTH, stack_frames, &count);
    if (count == 1)
    {
        printf("No stack trace!\n");
    }
    printf("Exception Stack Trace\n");
    printf("Stack Trace Depth: %d\n", count);
    for (i = 0; i < count; i++) {
        jvmtiFrameInfo stack_frame = stack_frames[i];
 
        /* ziskat jmeno methody i jeji tridu */
        (*jvmti_env)->GetMethodName(jvmti_env, stack_frame.method, &method_name_ptr, &method_signature_ptr, NULL);
        (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, stack_frame.method, &declaring_class);
        (*jvmti_env)->GetClassSignature(jvmti_env, declaring_class, &class_name_ptr, NULL);
 
        /* upravit jmeno tridy */
        updated_class_name_ptr = update_class_name(class_name_ptr, '.');
 
        /* provest vlastni vypis */
        printf("\tat %s%s() with signature %s\n", updated_class_name_ptr, method_name_ptr, method_signature_ptr);
 
        /* dealokace vsech ziskanych pametovych struktur */
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_name_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_signature_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)class_name_ptr);
    }
}
 
/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
    int line_number;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* ziskat cislo radku */
    line_number = get_line_number(jvmti_env, method, location);
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s() at ",
            updated_class_name_ptr,
            method_name_ptr);
    if (line_number == -1)
    {
        printf("unknown location\n");
    }
    else
    {
        printf("line %d\n", line_number);
    }
 
    if (catch_method == NULL)
    {
        print_stack_trace(jvmti_env, thr);
    }
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}
 
/*
 * Registrace callback funkce zavolane pri zachyceni vyjimky.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_EXCEPTION */
    callbacks.Exception = &callback_on_exception;
 
    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;
 
    /* zachytavat pouze udalost zachyceni vyjimky */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_EXCEPTION)) != 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");
}
 
/*
 * finito
 */

Agenta lze otestovat na javovské třídě Test16.java:

/**
  * Testovaci trida pouzita pro test sestnacteho
  * demonstracniho JVM TI agenta.
  */
public class Test16 {
 
    /**
      * Test zachyceni vyjimky typu ArrayIndexOutOfBoundsException.
      */
    public static void method1() {
        int[] a = {1,2,3};
        try {
            a[-1] = 10;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
      * Test zachyceni vyjimky typu ArithmeticException.
      */
    public static int method2(int a, int b) {
        try {
            return a/b;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }
 
    /**
      * Test zachyceni vyjimky typu ClassNotFoundException.
      */
    public static Class method3(String className) {
        try {
            return Class.forName(className);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
      * Test zachyceni vyjimky typu NullPointerException.
      */
    public static String method4(Object object) {
        try {
            return object.toString();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
 
    /**
      * Vyhozeni vyjimky.
      */
    public static String throwSomeException(Object object) {
        return object.toString();
    }
 
    public static void methodX() {
        methodY();
    }
 
    public static void methodY() {
        methodZ();
    }
 
    public static void methodZ() {
        throwSomeException(null);
    }
 
    /**
      * Spusteni testu.
      */
    public static void main(String[] args) {
        method1();
        method2(1, 0);
        method3("xyzzy");
        method4(null);
        methodX();
    }
}

Po spuštění testovací třídy s agentem s využitím příkazu:

java -agentpath:./libagent16.so Test16 2> /dev/null

Se na standardní výstup vypíšou následující zprávy:

Agent16: Agent_OnLoad
Agent16: JVM TI version is correct
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent16: catch exception of type java.lang.ArrayIndexOutOfBoundsException
    generated in method Test16.method1() at line 13
Agent16: catch exception of type java.lang.ArithmeticException
    generated in method Test16.method2() at line 25
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent16: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent16: catch exception of type java.lang.NullPointerException
    generated in method Test16.method4() at line 51
Agent16: uncatch exception of type java.lang.NullPointerException
    generated in method Test16.throwSomeException() at line 63
Exception Stack Trace
Stack Trace Depth: 5
        at Test16.throwSomeException() with signature (Ljava/lang/Object;)Ljava/lang/String;
        at Test16.methodZ() with signature ()V
        at Test16.methodY() with signature ()V
        at Test16.methodX() with signature ()V
        at Test16.main() with signature ([Ljava/lang/String;)V
Agent16: Agent_OnUnload

5. Přečtení čísla řádků ke všem metodám na zásobníku

Pokud porovnáme informace vypsané šestnáctým demonstračním agentem s výstupem metody Throwable.printStackTrace(), jednoduše zjistíme, že náš agent ještě postrádá dvě vlastnosti – nedokáže vypsat jméno zdrojového souboru a odpovídající číslo řádku, tj. přesná místa volání jednotlivých metod. Druhý nedostatek dokážeme velmi snadno napravit, protože již z předminula máme implementovanou pomocnou funkci get_line_number(), které se předávají tři parametry – instance jvmtiEnv (na tom není nic překvapivého), runtime identifikátor metody typu jmethodID a taktéž lokalizace instrukce uvnitř metody předávaná v parametru typu jlocation. A přesně tato dvojice informací: jmethodID+jlocation je obsahem struktury jvmtiFrameInfo vrácené funkcí GetStackTrace(). Vylepšení naší funkce print_stack_trace() tedy nebude složité, navíc ještě tuto funkci vylepšíme takovým způsobem, že si funkce sama zjistí počet zásobníkových rámců a nebude se spoléhat na konstantu MAX_STACK_TRACE_DEPTH:

/*
 * Vypis obsahu zasobniku.
 */
void print_stack_trace(
            jvmtiEnv *jvmti_env,
            jthread   thr)
{
    jvmtiFrameInfo *stack_frames;
    jclass declaring_class;
    int count;
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    int malloc_size;
    int frame_count;
    int i;
 
    /* Precist pocet zasobnikovych ramcu */
    (*jvmti_env)->GetFrameCount(jvmti_env, thr, &frame_count);
    printf("Stack Trace Depth: %d\n", frame_count);
    malloc_size = frame_count * sizeof(jvmtiFrameInfo);
    stack_frames = (jvmtiFrameInfo*)malloc(malloc_size);
    printf("(allocated %d bytes at address %p for storing stack frames info)\n", malloc_size, stack_frames);
 
    /* Ziskat trasovaci inforamce */
    (*jvmti_env)->GetStackTrace(jvmti_env, thr, 0, frame_count, stack_frames, &count);
    if (count == 1)
    {
        printf("No stack trace!\n");
    }
    printf("Exception Stack Trace\n");
    for (i = 0; i < count; i++) {
        int line_number;
        jvmtiFrameInfo stack_frame = stack_frames[i];
 
        /* ziskat jmeno metody i jeji tridu */
        (*jvmti_env)->GetMethodName(jvmti_env, stack_frame.method, &method_name_ptr, &method_signature_ptr, NULL);
        (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, stack_frame.method, &declaring_class);
        (*jvmti_env)->GetClassSignature(jvmti_env, declaring_class, &class_name_ptr, NULL);
 
        /* upravit jmeno tridy */
        updated_class_name_ptr = update_class_name(class_name_ptr, '.');
 
        /* ziskat cislo radku */
        line_number = get_line_number(jvmti_env, stack_frame.method, stack_frame.location);
 
        /* provest vlastni vypis */
        if (line_number >= 0)
        {
            printf("\tat %s%s(line:%d)\n", updated_class_name_ptr, method_name_ptr, line_number);
        }
        else
        {
            printf("\tat %s%s(unknown location)\n", updated_class_name_ptr, method_name_ptr);
        }
 
        /* dealokace vsech ziskanych pametovych struktur */
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_name_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_signature_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)class_name_ptr);
    }
    free(stack_frames);
}

6. Sedmnáctý demonstrační příklad – výpis volaných metod včetně čísel řádků

Nově upravenou a vylepšenou funkci print_stack_trace() využijeme v již sedmnácté verzi demonstračního JVM TI agenta, jehož zdrojový kód je vypsán pod tímto odstavcem:

/*
 * Sedmnacty demonstracni agent, ktery dokaze zaregistrovat
 * vznik * zachycovane i nezachycovane vyjimky a vypsat
 * stack trace spolecne s cislem radku volanych metod.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu k tomuto souboru
 * pres volbu -Icesta_k_jvm
 */
#include <jvmti.h>
 
/*
 * Jmeno agenta pouzite ve zpravach vypisovanych
 * na standardni vystup.
 */
#define AGENT_NAME "Agent17:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
 
/*
 * Vypis kodu chyby a chybove zpravy na standardni vystup.
 */
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 chybove hlaseni na standardni vystup.
 */
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 dve specialni schopnosti agenta */
    capabilities.can_generate_exception_events = 1;
    capabilities.can_get_line_numbers = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Uprava jmena tridy pro tisk.
 */
char* update_class_name(char *class_name_ptr, char replace_to)
{
    char *class_name_ptr_;
    char *c;
    if (class_name_ptr != NULL)
    {
        /* odstraneni pocatecniho L na zacatku jmena tridy */
        class_name_ptr_ = class_name_ptr;
        if (class_name_ptr_[0] == 'L')
        {
            class_name_ptr_++;
        }
        /* nahrada znaku ; za tecku ci jiny definovany znak */
        char *last_char = class_name_ptr_ + strlen(class_name_ptr_) - 1;
        if (*last_char == ';')
        {
            *last_char = replace_to;
        }
        /* nahrada vsech znaku '/' za '.' */
        c = class_name_ptr_;
        for (c = class_name_ptr_; *c != 0; c++)
        {
            if (*c == '/') *c = '.';
        }
    }
    return class_name_ptr_;
}
 
/*
 * Ziskani cisla radku pro zadanou metodu a index instrukce.
 */
int get_line_number(jvmtiEnv *jvmti_env, jmethodID method, jlocation location)
{
    int count;
    int line_number = 0;
    int i;
    jvmtiLineNumberEntry *location_table;
    jvmtiError error_code;
 
    if (method == NULL)
    {
        return -1;
    }
 
    /* nacteni tabulky s cisly radku a indexy instrukci */
    error_code = (*jvmti_env)->GetLineNumberTable(jvmti_env, method, &count, &location_table);
    /* v nekterych pripadech se nacist tabulku nepodari */
    if (error_code != JVMTI_ERROR_NONE)
    {
        return -1;
    }
 
    /* projit celou tabulkou */
    for (i = 0; i < count - 1; i++)
    {
        jvmtiLineNumberEntry entry1 = location_table[i];
        jvmtiLineNumberEntry entry2 = location_table[i+1];
        /* pokud se lokace nachazi mezi entry1 (vcetne) a entry2 (krome)
         * nasli jsme spravny radek */
        if (location >= entry1.start_location && location < entry2.start_location)
        {
            line_number = entry1.line_number;
            break;
        }
    }
    /* take se muze jednat o uplne posledni instrukci v metode */
    if (location >= location_table[count-1].start_location)
    {
        line_number = location_table[count-1].line_number;
    }
 
    /* dealokace tabulky */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)location_table);
    return line_number;
}
 
/*
 * Vypis obsahu zasobniku.
 */
void print_stack_trace(
            jvmtiEnv *jvmti_env,
            jthread   thr)
{
    jvmtiFrameInfo *stack_frames;
    jclass declaring_class;
    int count;
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    int malloc_size;
    int frame_count;
    int i;
 
    /* Precist pocet zasobnikovych ramcu */
    (*jvmti_env)->GetFrameCount(jvmti_env, thr, &frame_count);
    printf("Stack Trace Depth: %d\n", frame_count);
    malloc_size = frame_count * sizeof(jvmtiFrameInfo);
    stack_frames = (jvmtiFrameInfo*)malloc(malloc_size);
    printf("(allocated %d bytes at address %p for storing stack frames info)\n", malloc_size, stack_frames);
 
    /* Ziskat trasovaci inforamce */
    (*jvmti_env)->GetStackTrace(jvmti_env, thr, 0, frame_count, stack_frames, &count);
    if (count == 1)
    {
        printf("No stack trace!\n");
    }
    printf("Exception Stack Trace\n");
    for (i = 0; i < count; i++) {
        int line_number;
        jvmtiFrameInfo stack_frame = stack_frames[i];
 
        /* ziskat jmeno metody i jeji tridu */
        (*jvmti_env)->GetMethodName(jvmti_env, stack_frame.method, &method_name_ptr, &method_signature_ptr, NULL);
        (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, stack_frame.method, &declaring_class);
        (*jvmti_env)->GetClassSignature(jvmti_env, declaring_class, &class_name_ptr, NULL);
 
        /* upravit jmeno tridy */
        updated_class_name_ptr = update_class_name(class_name_ptr, '.');
 
        /* ziskat cislo radku */
        line_number = get_line_number(jvmti_env, stack_frame.method, stack_frame.location);
 
        /* provest vlastni vypis */
        if (line_number >= 0)
        {
            printf("\tat %s%s(line:%d)\n", updated_class_name_ptr, method_name_ptr, line_number);
        }
        else
        {
            printf("\tat %s%s(unknown location)\n", updated_class_name_ptr, method_name_ptr);
        }
 
        /* dealokace vsech ziskanych pametovych struktur */
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_name_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)method_signature_ptr);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)class_name_ptr);
    }
    free(stack_frames);
}
 
/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
    int line_number;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* ziskat cislo radku */
    line_number = get_line_number(jvmti_env, method, location);
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s() at ",
            updated_class_name_ptr,
            method_name_ptr);
    if (line_number == -1)
    {
        printf("unknown location\n");
    }
    else
    {
        printf("line %d\n", line_number);
    }
 
    if (catch_method == NULL)
    {
        print_stack_trace(jvmti_env, thr);
    }
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}
 
/*
 * Registrace callback funkce zavolane pri zachyceni vyjimky.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_EXCEPTION */
    callbacks.Exception = &callback_on_exception;
 
    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;
 
    /* zachytavat pouze udalost zachyceni vyjimky */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_EXCEPTION)) != 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");
}
 
/*
 * finito
 */

Agenta otestujeme na nové variantě javovské třídy, která bude mít tentokrát (nikoli překvapivě) název Test17:

/**
  * Testovaci trida pouzita pro test sedmnacteho
  * demonstracniho JVM TI agenta.
  */
public class Test17 {
 
    /**
      * Test zachyceni vyjimky typu ArrayIndexOutOfBoundsException.
      */
    public static void method1() {
        int[] a = {1,2,3};
        try {
            a[-1] = 10;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
      * Test zachyceni vyjimky typu ArithmeticException.
      */
    public static int method2(int a, int b) {
        try {
            return a/b;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }
 
    /**
      * Test zachyceni vyjimky typu ClassNotFoundException.
      */
    public static Class method3(String className) {
        try {
            return Class.forName(className);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
      * Test zachyceni vyjimky typu NullPointerException.
      */
    public static String method4(Object object) {
        try {
            return object.toString();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
 
    /**
      * Vyhozeni vyjimky.
      */
    public static String throwSomeException(Object object) {
        return object.toString();
    }
 
    public static void methodX() {
        methodY();
    }
 
    public static void methodY() {
        methodZ();
    }
 
    public static void methodZ() {
        methodW();
    }
 
    public static void methodW() {
        throwSomeException(null);
    }
 
    /**
      * Spusteni testu.
      */
    public static void main(String[] args) {
        method1();
        method2(1, 0);
        method3("xyzzy");
        method4(null);
        methodX();
    }
}

Podívejme se, zda a jak vůbec se rozšířily informace vypisované agentem:

Agent17: Agent_OnLoad
Agent17: JVM TI version is correct
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent17: catch exception of type java.lang.ArrayIndexOutOfBoundsException
    generated in method Test17.method1() at line 13
Agent17: catch exception of type java.lang.ArithmeticException
    generated in method Test17.method2() at line 25
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent17: catch exception of type java.lang.NullPointerException
    generated in method Test17.method4() at line 51
Agent17: uncatch exception of type java.lang.NullPointerException
    generated in method Test17.throwSomeException() at line 63
Stack Trace Depth: 6
(allocated 72 bytes at address 0x8cf2520 for storing stack frames info)
Exception Stack Trace
        at Test17.throwSomeException(line:63)
        at Test17.methodW(line:79)
        at Test17.methodZ(line:75)
        at Test17.methodY(line:71)
        at Test17.methodX(line:67)
        at Test17.main(line:90)
Agent17: Agent_OnUnload

Skutečně se ve výpisu objevila čísla řádků. Ještě otestujeme, zda je správně detekován stav, kdy čísla řádků nelze zjistit, například tehdy, pokud je volána nativní metoda. Agenta jednoduše upravíme tak, aby vypisovat stack trace pro všechny výjimky, tj. i výjimky zachycované:

Agent17: Agent_OnLoad
Agent17: JVM TI version is correct
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Stack Trace Depth: 6
(allocated 72 bytes at address 0x8514580 for storing stack frames info)
Exception Stack Trace
        at java.lang.ClassLoader.findBootstrapClass(unknown location)
        at java.lang.ClassLoader.findBootstrapClass0(line:900)
        at java.lang.ClassLoader.loadClass(line:316)
        at java.lang.ClassLoader.loadClass(line:314)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Stack Trace Depth: 7
(allocated 84 bytes at address 0x85375a0 for storing stack frames info)
Exception Stack Trace
        at java.net.URLClassLoader$1.run(line:217)
        at java.security.AccessController.doPrivileged(unknown location)
        at java.net.URLClassLoader.findClass(line:205)
        at java.lang.ClassLoader.loadClass(line:321)
        at java.lang.ClassLoader.loadClass(line:314)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Stack Trace Depth: 5
(allocated 60 bytes at address 0x8536f18 for storing stack frames info)
Exception Stack Trace
        at java.net.URLClassLoader.findClass(line:222)
        at java.lang.ClassLoader.loadClass(line:321)
        at java.lang.ClassLoader.loadClass(line:314)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
Agent17: catch exception of type java.lang.ArrayIndexOutOfBoundsException
    generated in method Test17.method1() at line 13
Stack Trace Depth: 2
(allocated 24 bytes at address 0x8536ef0 for storing stack frames info)
Exception Stack Trace
        at Test17.method1(line:13)
        at Test17.main(line:86)
Agent17: catch exception of type java.lang.ArithmeticException
    generated in method Test17.method2() at line 25
Stack Trace Depth: 2
(allocated 24 bytes at address 0x8536ef0 for storing stack frames info)
Exception Stack Trace
        at Test17.method2(line:25)
        at Test17.main(line:87)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Stack Trace Depth: 10
(allocated 120 bytes at address 0x85373b0 for storing stack frames info)
Exception Stack Trace
        at java.lang.ClassLoader.findBootstrapClass(unknown location)
        at java.lang.ClassLoader.findBootstrapClass0(line:900)
        at java.lang.ClassLoader.loadClass(line:316)
        at java.lang.ClassLoader.loadClass(line:314)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
        at java.lang.Class.forName0(unknown location)
        at java.lang.Class.forName(line:186)
        at Test17.method3(line:38)
        at Test17.main(line:88)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Stack Trace Depth: 11
(allocated 132 bytes at address 0x8579f78 for storing stack frames info)
Exception Stack Trace
        at java.net.URLClassLoader$1.run(line:217)
        at java.security.AccessController.doPrivileged(unknown location)
        at java.net.URLClassLoader.findClass(line:205)
        at java.lang.ClassLoader.loadClass(line:321)
        at java.lang.ClassLoader.loadClass(line:314)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
        at java.lang.Class.forName0(unknown location)
        at java.lang.Class.forName(line:186)
        at Test17.method3(line:38)
        at Test17.main(line:88)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Stack Trace Depth: 9
(allocated 108 bytes at address 0x8536f28 for storing stack frames info)
Exception Stack Trace
        at java.net.URLClassLoader.findClass(line:222)
        at java.lang.ClassLoader.loadClass(line:321)
        at java.lang.ClassLoader.loadClass(line:314)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
        at java.lang.Class.forName0(unknown location)
        at java.lang.Class.forName(line:186)
        at Test17.method3(line:38)
        at Test17.main(line:88)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Stack Trace Depth: 10
(allocated 120 bytes at address 0x85373b0 for storing stack frames info)
Exception Stack Trace
        at java.net.URLClassLoader$1.run(line:217)
        at java.security.AccessController.doPrivileged(unknown location)
        at java.net.URLClassLoader.findClass(line:205)
        at java.lang.ClassLoader.loadClass(line:321)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
        at java.lang.Class.forName0(unknown location)
        at java.lang.Class.forName(line:186)
        at Test17.method3(line:38)
        at Test17.main(line:88)
Agent17: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Stack Trace Depth: 8
(allocated 96 bytes at address 0x8536f28 for storing stack frames info)
Exception Stack Trace
        at java.net.URLClassLoader.findClass(line:222)
        at java.lang.ClassLoader.loadClass(line:321)
        at sun.misc.Launcher$AppClassLoader.loadClass(line:294)
        at java.lang.ClassLoader.loadClass(line:266)
        at java.lang.Class.forName0(unknown location)
        at java.lang.Class.forName(line:186)
        at Test17.method3(line:38)
        at Test17.main(line:88)
Agent17: catch exception of type java.lang.NullPointerException
    generated in method Test17.method4() at line 51
Stack Trace Depth: 2
(allocated 24 bytes at address 0x8537430 for storing stack frames info)
Exception Stack Trace
        at Test17.method4(line:51)
        at Test17.main(line:89)
Agent17: uncatch exception of type java.lang.NullPointerException
    generated in method Test17.throwSomeException() at line 63
Stack Trace Depth: 6
(allocated 72 bytes at address 0x8536f28 for storing stack frames info)
Exception Stack Trace
        at Test17.throwSomeException(line:63)
        at Test17.methodW(line:79)
        at Test17.methodZ(line:75)
        at Test17.methodY(line:71)
        at Test17.methodX(line:67)
        at Test17.main(line:90)
Agent17: Agent_OnUnload

7. Analýza obsahu haldy (heapu)

Konečně se dostáváme k popisu jedné z nejsložitějších funkcí, které v rozhraní JVM TI existují. Jedná se o funkci nazvanou IterateThroughHeap(), kterou je možné využít pro procházení všech objektů či pouze vybraných typů objektů uložených na haldě (heapu). V rozhraní JVM TI totiž nejsou k dispozici funkce, které by dokázaly přímo vrátit seznam objektů vyfiltrovaný na základě nějaké podmínky, namísto toho musí programátor tento seznam zpracovat v rámci callback funkcí zavolaných pro každý objekt nalezený na heapu (to znamená, že rozdíl mezi prvním a druhým způsobem zhruba odpovídá rozdílu mezi DOM a SAX). Vlastně je to logické, protože informace získané o objektech by mohly zabrat více místa, než je kapacita celé haldy. Pokud tedy programátor potřebuje zjistit informace o určitých typech objektů na haldě, musí zaregistrovat příslušné callback funkce, kterým se budou tyto informace předávat a posléze zavolat funkci:

jvmtiError IterateThroughHeap(
            jvmtiEnv* env,
            jint heap_filter,
            jclass klass,
            const jvmtiHeapCallbacks* callbacks,
            const void* user_data)

Význam prvního parametru této funkce je zřejmý. Zajímavější je druhý parametr, přes který je možné specifikovat základní filtr, který určuje, pro které typy objektů se budou callback funkce volat. Využít je možné následující hodnoty, popř. jejich bitové kombinace:

Konstanta Hodnota Význam
JVMTI_HEAP_FILTER_TAGGED 0×04 objekty s přiřazeným tagem jsou odfiltrovány
JVMTI_HEAP_FILTER_UNTAGGED 0×08 objekty bez tagu jsou odfiltrovány
JVMTI_HEAP_FILTER_CLASS_TAGGED 0×10 objekty, jejichž třídy mají přiřazeny tag jsou odfiltrovány
JVMTI_HEAP_FILTER_CLASS_UNTAGGED 0×20 přesný opak předchozí konstanty

Jménem tag je zde myšlen celočíselný údaj rozdílný od nuly, kterým mohou být objekty označeny, a to opět pomocí rozhraní JVM TI. Podrobnosti si popíšeme příště.

Ve třetím parametru lze předat buď hodnotu NULL popř. identifikátor třídy, který je opět použit pro filtraci. To například znamená, že pokud nás zajímají informace pouze o instancích třídy org.foo.bar.Baz, lze toho velmi jednoduše docílit, aniž by se musel procházet celý heap, resp.  aniž by byla příslušná callback funkce volána pro všechny instance ostatních tříd.

Třetím parametrem je ukazatel na strukturu jvmtiHeapCallbacks. Právě sem může programátor uložit odkazy na callback funkce:

widgety

typedef struct {
    jvmtiHeapIterationCallback heap_iteration_callback;
    jvmtiHeapReferenceCallback heap_reference_callback;
    jvmtiPrimitiveFieldCallback primitive_field_callback;
    jvmtiArrayPrimitiveValueCallback array_primitive_value_callback;
    jvmtiStringPrimitiveValueCallback string_primitive_value_callback;
    jvmtiReservedCallback reserved5;
    jvmtiReservedCallback reserved6;
    jvmtiReservedCallback reserved7;
    jvmtiReservedCallback reserved8;
    jvmtiReservedCallback reserved9;
    jvmtiReservedCallback reserved10;
    jvmtiReservedCallback reserved11;
    jvmtiReservedCallback reserved12;
    jvmtiReservedCallback reserved13;
    jvmtiReservedCallback reserved14;
    jvmtiReservedCallback reserved15;
} jvmtiHeapCallbacks;

Přesnější význam této struktury si vysvětlíme v následující části tohoto seriálu.

8. Zdrojové kódy demonstračních příkladů

Podobně jako minule, i dnešní demonstrační příklady byly kvůli snazšímu udržování všech zdrojových kódů uloženy do Mercurial repositáře, jenž je dostupný na adrese http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/. Poslední verze dnes popisovaných JVM TI agentů i další potřebné skripty jsou dostupné na následujících adresách:

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?
Vitalia.cz: Test dětských svačinek: Tyhle ne!

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

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

Sony MP-CL1A: miniaturní projektor

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

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

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

Vitalia.cz: Fyzioterapeutka: Chůze naboso? Rozhodně ano!

Fyzioterapeutka: Chůze naboso? Rozhodně ano!

Podnikatel.cz: Takhle se prodávají mražené potraviny

Takhle se prodávají mražené potraviny

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

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

DigiZone.cz: Samsung EVO-S: novinka pro Skylink

Samsung EVO-S: novinka pro Skylink

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

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

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

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

DigiZone.cz: Na jaká videa se vlastně díváme

Na jaká videa se vlastně díváme

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

Znáte už 5 novinek k #EET

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

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

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

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

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

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

dTest odhalil ten nejlepší kečup

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

Jak levné procesory změnily svět?

DigiZone.cz: Digi Slovakia zařazuje stanice SPI

Digi Slovakia zařazuje stanice SPI

Lupa.cz: Aukro.cz mění majitele. Vrací se do českých rukou

Aukro.cz mění majitele. Vrací se do českých rukou