Obsah
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ů
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.org/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:
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.org/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:
Demonstrační příklad | Umístění |
---|---|
Agent #16 | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/d031ab021294/jvmti-agents/agent16/ |
Agent #17 | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/d031ab021294/jvmti-agents/agent17 |
9. Odkazy na Internetu
- The JVM Tool Interface (JVM TI): How VM Agents Work
http://www.oracle.com/technetwork/articles/javase/jvm-ti-141370.html - JVM Tool Interface Version 1.2
http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - ClojureScript One: Index
http://clojurescriptone.com/index.html - ClojureScript One: Documentation
http://clojurescriptone.com/documentation.html - ClojureScript One: Wiki
https://github.com/brentonashworth/one/wiki - ClojureScript: Quick Start
https://github.com/clojure/clojurescript/wiki/Quick-Start - Getting Started with ClojureScript (and FW/1)
http://corfield.org/entry/getting-started-with-clojurescript-and-fw-1 - First ClojureScript experiences: using Raphaël
http://maurits.wordpress.com/2012/02/13/first-clojurescript-experiences-using-raphael/ - Raphaël-JavaScript Library
http://raphaeljs.com/ - A detailed installation Guide for VimClojure 2.2
http://www.duenas.at/new_homepage/vimclojure - VimClojure : A filetype, syntax and indent plugin for Clojure
http://www.vim.org/scripts/script.php?script_id=2501 - Nailgun server
http://www.martiansoftware.com/nailgun/background.html - SLIME (Wikipedia)
http://en.wikipedia.org/wiki/SLIME - slime.vim
http://s3.amazonaws.com/mps/slime.vim - Textový editor Vim jako IDE: 1. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide/ - Textový editor Vim jako IDE: 2. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-2-cast/ - Textový editor Vim jako IDE: 3. část (omni completion)
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-3-cast/ - Textový editor Vim jako IDE: 4. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-4-cast/ - Textový editor Vim jako IDE: 5. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-5-cast/ - Textový editor Vim jako IDE: 6. část – Vim Script
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-6-cast-vim-script/ - Textový editor Vim jako IDE: 7. část – Vim Script
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-7-cast-vim-script/ - Textový editor Vim jako IDE: 8. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-8-cast/ - 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/ - 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/ - Přenos textů mezi Vimem a dalšími aplikacemi
http://www.root.cz/clanky/prenos-textu-mezi-vimem-a-dalsimi-aplikacemi/ - 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/ - Textový editor Vim: automatické formátování textů
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-14-cast-automaticke-formatovani-textu/ - Textový editor Vim: automatické formátování textů
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-automaticke-formatovani-textu-dokonceni/ - Textový editor Vim: editace XML a HTML
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-15-cast-editace-xml-a-html/ - 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/ - 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/ - Textový editor Vim: konfigurace a překlad Vimu
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-18-cast-konfigurace-a-preklad-vimu/ - Counterclockwise
http://code.google.com/p/counterclockwise/ - Clojure IDEs – The Grand Tour
http://www.bestinclass.dk/index.clj/2010/03/clojure-ides-the-grand-tour-getting-started.html - Light Table – a new IDE concept
http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/ - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Čistě funkcionální (datové struktury, jazyky, programování)
http://cs.wikipedia.org/wiki/Čistě_funkcionální - 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 - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Eulerovo číslo
http://cs.wikipedia.org/wiki/Eulerovo_číslo - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Clojure.org: Clojure home page
http://clojure.org/downloads - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - A Couple of Clojure Agent Examples
http://lethain.com/a-couple-of-clojure-agent-examples/ - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html