Hlavní navigace

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

Pavel Tišnovský 20. 11. 2012

Dnes se již potřetí budeme zabývat popisem rozhraní JVM TI, které je možné využít pro tvorbu takzvaných agentů monitorujících, popř. ovlivňujících chování aplikace v JVM. Příklady uvedené v dnešní části budou velmi jednoduché, protože si ukážeme použití funkcí pro čtení a změnu obsahu systémových proměnných.

Obsah

1. Systémové proměnné dostupné agentům: funkce GetSystemProperty()

2. Příklad použití funkce GetSystemProperty()

3. Sedmý demonstrační příklad: agent, který přečte hodnoty vybraných systémových proměnných

4. Změna hodnot systémových proměnných: funkce SetSystemProperty()

5. Osmý demonstrační příklad: změna nastavení CLASSPATH v době spuštění agenta

6. Devátý demonstrační příklad: pokus o změnu nemodifikovatelné proměnné

7. Funkce GetSystemProperties() určená pro čtení názvů všech systémových proměnných

8. Desátý demonstrační příklad: výpis všech systémových proměnných nastavených virtuálním strojem

9. Odkazy na Internetu

1. Systémové proměnné dostupné agentům: funkce GetSystemProperty()

V předchozích dvou částech tohoto seriálu jsme si popsali způsob využití rozhraní JVM TI (JVM Tool Interface), které se využívá k různým činnostem, zejména pro sledování a taktéž pro ovlivňování činnosti běhu virtuálního stroje Javy. Minule jste si taktéž ukázali způsob registrace callback funkcí v JVM TI agentech. Tyto funkce, pokud jsou ovšem korektně zaregistrovány a je provedena náležitá konfigurace agenta, jsou zavolány například při zahájení či ukončení běhu správce paměti (GC – garbage collector), ve chvíli, kdy je zavolána nějaká javovská metoda, popř. při vzniku zachycené či nezachycené výjimky (typů callback funkcí však existuje mnohem více, některé typy si popíšeme příště). Dnešní část seriálu o JVM bude poněkud jednodušší, protože si „pouze“ ukážeme, jakým způsobem může agent číst popř. měnit hodnotu některých systémových proměnných (system properties), které jsou využívané virtuálním strojem Javy a v některých případech i samotnou javovskou aplikací – příkladem je aplikace načítající nějakou proměnnou přes metodu System.getProperty(), popř. System.getProperties().

JVM TI agent může načíst hodnotu libovolné systémové proměnné s využitím funkce nazvané jednoduše GetSystemProperty(), jejíž hlavička je následující:

jvmtiError GetSystemProperty(
        jvmtiEnv* env,
        const char* property,
        char** value_ptr)

Jak je z této hlavičky patrné, předávají se funkci GetSystemProperty() tři parametry. První parametr je nám již dobře známý – jedná se o ukazatel na strukturu obsahující stav „prostředí“ agenta. Tato struktura je předávána do většiny callback funkcí a v našem případě je důležité to, že ji můžeme získat ve funkci Agent_OnLoad() po zavolání GetEnv(). Druhým parametrem je název systémové proměnné (konstantní řetězec) a třetím parametrem je ukazatel na céčkový řetězec, který je automaticky alokovaný a naplněný přímo funkcí GetSystemProperty() hodnotou zvolené systémové proměnné. To znamená, že nemusíme dopředu alokovat místo pro tento řetězec a současně je nutné (nejlépe co nejdříve) tento řetězec uvolnit s využitím funkce Deallocate() popsané podrobněji v předchozí části tohoto seriálu. Každý virtuální stroj Javy by měl nastavovat přinejmenším následujících šest systémových proměnných:

java.vm.vendor
java.vm.version
java.vm.name
java.vm.info
java.library.path
java.class.path

Ve skutečnosti je těchto proměnných nastaveno více, minimálně v případě OpenJDK a Oracle JDK.

2. Příklad použití funkce GetSystemProperty()

Ukažme si nyní, jakým způsobem lze načíst a vypsat vybrané systémové proměnné. To se přímo v agentovi provede poměrně snadno, musíme ovšem mít na mysli, že funkci GetSystemProperty() je možné zavolat pouze ve fázi načítání a inicializace agenta, tj. buď přímo ve fázi inicializace virtuálního stroje Javy, popř. ve chvíli, kdy je agent připojován k již běžícímu virtuálnímu stroji (to jsme si ovšem ještě neukazovali). Pro nás to znamená, že budeme funkci GetSystemProperty() volat z funkce Agent_OnLoad(), což je funkce, která je automaticky zavolaná ve chvíli inicializace samotného agenta. V této chvíli má agent možnost si systémové proměnné přečíst a popřípadě některé z nich i modifikovat. Funkce (zavolaná z Agent_OnLoad(), která vypíše obsah dvanácti základních systémových proměnných nabízených OpenJDK i Oracle JDK, může ve své nejjednodušší podobě vypadat následovně. Povšimněte si, že ihned po vypsání hodnoty systémové proměnné provedeme dealokaci řetězce s využitím funkce Deallocate():

/*
 * Tisk nekterych vybranych systemovych promennych.
 */
static void print_selected_system_properties(jvmtiEnv *jvmti_env)
{
    /* pole se jmeny promennych - nutno ukoncit NULLem! */
    static char* property_names[] =
    {
        "sun.java.command",
        "java.home",
        "java.class.path",
        "java.library.path",
        "sun.boot.class.path",
        "sun.boot.library.path",
        "java.ext.dirs",
        "java.endorsed.dirs",
        "java.vm.version",
        "java.vm.name",
        "java.vm.info",
        "java.vm.vendor",
        NULL
    };
 
    MSG("---  System properties  ---");
 
    /* ziskame a vypiseme hodnoty vybranych systemovych promennych */
    char **property_name = property_names;
    for (; *property_name != NULL; property_name++)
    {
        char *property_value = NULL;
        (*jvmti_env)->GetSystemProperty(jvmti_env, *property_name, &property_value);
        printf("%-30s\t%s\n", *property_name, property_value);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
    }
    MSG("--- --- --- --- --- --- ---");
}

V případě, že nějaká systémová proměnná neexistuje nebo ji nelze z nějakého důvodu přečíst, vrátí funkce GetSystemProperty() chybový kód (JVMTI_ERROR_NOT_AVAILABLE, JVMTI_ERROR_NULL_POINTER), který je vhodné korektně testovat, což znamená, že by naše funkce pro čtení systémových proměnných mohla vypadat spíše následovně – kontrolujeme zde jak návratový kód funkce GetSystemProperty(), tak pro úplnou jistotu i návratový kód funkce Deallocate():

/*
 * Tisk nekterych vybranych systemovych promennych.
 */
static void print_selected_system_properties(jvmtiEnv *jvmti_env)
{
    /* pole se jmeny promennych - nutno ukoncit NULLem! */
    static char* property_names[] =
    {
        "sun.java.command",
        "java.home",
        "java.class.path",
        "java.library.path",
        "sun.boot.class.path",
        "sun.boot.library.path",
        "java.ext.dirs",
        "java.endorsed.dirs",
        "java.vm.version",
        "java.vm.name",
        "java.vm.info",
        "java.vm.vendor",
        NULL
    };
 
    jvmtiError error_code;
 
    MSG("---  System properties  ---");
 
    /* ziskame a vypiseme hodnoty vybranych systemovych promennych */
    char **property_name = property_names;
    for (; *property_name != NULL; property_name++)
    {
        char *property_value = NULL;
        error_code = (*jvmti_env)->GetSystemProperty(jvmti_env, *property_name, &property_value);
        check_jvmti_error(jvmti_env, error_code, "GetSystemProperty");
        printf("%-30s\t%s\n", *property_name, property_value);
        error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
        check_jvmti_error(jvmti_env, error_code, "Deallocate");
    }
    MSG("--- --- --- --- --- --- ---");
}

3. Sedmý demonstrační příklad: agent, který přečte hodnoty vybraných systémových proměnných

S využitím informací, které jsme získali v prvníve druhé kapitole již můžeme napsat zdrojový kód celého agenta. V porovnání se zdrojovými kódy z předchozí části tohoto seriálu je agent velmi jednoduchý, protože nemusíme nastavovat žádné jeho požadované vlastnosti ani není zapotřebí registrovat callback funkce:

/*
 * JVM TI agent, ktery na standardni vystup vypise hodnoty
 * nekterych vybranych systemovych promennych.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent7:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Tisk nekterych vybranych systemovych promennych.
 */
static void print_selected_system_properties(jvmtiEnv *jvmti_env)
{
    /* pole se jmeny promennych - nutno ukoncit NULLem! */
    static char* property_names[] =
    {
        "sun.java.command",
        "java.home",
        "java.class.path",
        "java.library.path",
        "sun.boot.class.path",
        "sun.boot.library.path",
        "java.ext.dirs",
        "java.endorsed.dirs",
        "java.vm.version",
        "java.vm.name",
        "java.vm.info",
        "java.vm.vendor",
        NULL
    };
 
    jvmtiError error_code;
 
    MSG("---  System properties  ---");
 
    /* ziskame a vypiseme hodnoty vybranych systemovych promennych */
    char **property_name = property_names;
    for (; *property_name != NULL; property_name++)
    {
        char *property_value = NULL;
        error_code = (*jvmti_env)->GetSystemProperty(jvmti_env, *property_name, &property_value);
        check_jvmti_error(jvmti_env, error_code, "GetSystemProperty");
        printf("%-30s\t%s\n", *property_name, property_value);
        error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
        check_jvmti_error(jvmti_env, error_code, "Deallocate");
    }
    MSG("--- --- --- --- --- --- ---");
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
 
    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");
 
    /* Vytiskneme vybrane systemove promenne. */
    print_selected_system_properties(jvmti);
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}

Funkci tohoto agenta je možné si otestovat na tomto jednoduchém javovském programu:

/**
 * Vypise na standardni vystup hodnotu systemove promenne "java.vm.vendor"
 */
public class Test7 {
 
    public static void main(String[] args) {
        System.out.println("--- Java side ---");
        System.out.format("%-30s\t%s\n", "java.vm.vendor", System.getProperty("java.vm.vendor"));
        System.out.println("--- Java side ---");
    }
 
}

Po překladu obou programů:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent7.so agent7.c
javac Test7.java

již můžeme spustit JVM i s agentem, a to příkazem:

java -agentpath:./libagent7.so Test7

Následuje příklad výpisu, v němž můžeme vidět jak obsah všech zvolených systémových proměnných přečtených agentem, tak i výstup testovacího javovského programu. Konkrétní hodnoty v tomto případě samozřejmě budou na různých systémech odlišné, protože záleží na konkrétní konfiguraci Javy, její verzi, nastavení „alternatives“ atd. (zvýrazněním je naznačeno, že jak agent, tak i javovský program dostane shodné hodnoty):

Agent7: Agent_OnLoad
Agent7: JVM TI version is correct
Agent7: ---  System properties  ---
sun.java.command                Test7
java.home                       /usr/lib/jvm/java-6-openjdk/jre
java.class.path                 .
java.library.path               /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib
sun.boot.class.path             /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes
sun.boot.library.path           /usr/lib/jvm/java-6-openjdk/jre/lib/i386
java.ext.dirs                   /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
java.endorsed.dirs              /usr/lib/jvm/java-6-openjdk/jre/lib/endorsed
java.vm.version                 20.0-b12
java.vm.name                    OpenJDK Client VM
java.vm.info                    mixed mode, sharing
java.vm.vendor                  Sun Microsystems Inc.
Agent7: --- --- --- --- --- --- ---
--- Java side ---
java.vm.vendor                  Sun Microsystems Inc.
--- Java side ---
Agent7: Agent_OnUnload

4. Změna hodnot systémových proměnných: funkce SetSystemProperty()

Přečtení obsahu vybrané systémové proměnné může být pro agenta velmi důležité, protože z obsahu některých proměnných například může získat informaci o tom, kde hledat nějakou knihovnu atd. (na straně javovských programů se to děje poloautomaticky přes CLASSPATH, v případě agenta psaného většinou v céčku či C++ to je však poněkud složitější). Ovšem může nastat situace, kdy bude nutné nějakou proměnnou změnit. I to je samozřejmě možné, ovšem (opět) pouze ve fázi inicializace samotného virtuálního stroje Javy – není tedy žádoucí (a mnoho implementací JVM TI to hlídá), aby agent měnil systémové proměnné prakticky kdykoli javovské aplikaci „pod rukama“. Pro změnu hodnoty některé systémové proměnné lze v agentovi využít funkci nazvanou, jak již zajisté očekáváte, SetSystemProperty(), jejíž hlavička vypadá následovně:

jvmtiError SetSystemProperty(
        jvmtiEnv* env,
        const char* property,
        const char* value)

Této funkci se opět předávají tři parametry. Prvním parametrem je ukazatel na strukturu obsahující stav „prostředí“ agenta, ve druhém parametru musí být předán řetězec s názvem nějaké systémové proměnné a v parametru třetím se opět předává řetězec, tentokrát s novou hodnotou systémové proměnné. Jak si ukážeme dále, je možné měnit pouze některé systémové proměnné a pokud se agent pokusí modifikovat některou proměnnou určenou pouze pro čtení, vrátí funkce SetSystemProperty() chybový kód JVMTI_ERROR_NOT_AVAILABLE, který znamená, že daná systémová proměnná buď vůbec neexistuje nebo je možné její obsah pouze přečíst, ale nikoli přepsat. Jednou z proměnných – a to velmi důležitých proměnných – je i proměnná java.class.path, která obsahuje cestu, na nichž JVM hledá třídy aplikace. Tuto proměnnou lze změnit a pokud ji změníme například tak, že zde nebude obsažena tečka (aktuální adresář), dojde při inicializaci aplikace k chybě, což si můžete vyzkoušet v demonstračním příkladu uvedeném v navazující kapitole.

Funkce pro změnu systémové proměnné nazvané java.class.path může být velmi jednoduchá, jak je to ostatně patrné při pohledu na následující funkci, kterou využijeme i v osmém demonstračním příkladu popsaném v páté kapitole:

/*
 * Zmena jedne vybrane promenne.
 */
static void change_selected_property(jvmtiEnv *jvmti_env)
{
    jvmtiError error_code;
    error_code = (*jvmti_env)->SetSystemProperty(jvmti_env, "java.class.path", ".:new_path");
    check_jvmti_error(jvmti_env, error_code, "SetSystemProperty");
}

5. Osmý demonstrační příklad: změna nastavení CLASSPATH v době spuštění agenta

Naši funkci change_selected_property() z předchozí kapitoly využijeme prakticky, tentokrát v osmém demonstračním agentovi, jehož zdrojový kód je vypsán pod tímto odstavcem. Agent nejdříve načte a vypíše hodnoty všech vybraných systémových proměnných, potom jednu zvolenou proměnnou (java.class.path) změní a posléze opět vypíše hodnoty vybraných systémových proměnných – zde se již ovšem promítne změna v jedné proměnné:

/*
 * JVM TI agent, ktery na standardni vystup vypise hodnoty
 * nekterych vybranych systemovych promennych, posleze
 * zmeni jednu promennou, ktera je nasledne prectena z programu
 * beziciho v JVM.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent8:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Tisk nekterych vybranych systemovych promennych.
 */
static void print_selected_system_properties(jvmtiEnv *jvmti_env)
{
    /* pole se jmeny promennych - nutno ukoncit NULLem! */
    static char* property_names[] =
    {
        "sun.java.command",
        "java.home",
        "java.class.path",
        "java.library.path",
        "sun.boot.class.path",
        "sun.boot.library.path",
        "java.ext.dirs",
        "java.endorsed.dirs",
        "java.vm.version",
        "java.vm.name",
        "java.vm.info",
        "java.vm.vendor",
        NULL
    };
 
    jvmtiError error_code;
 
    MSG("---  System properties  ---");
 
    /* ziskame a vypiseme hodnoty vybranych systemovych promennych */
    char **property_name = property_names;
    for (; *property_name != NULL; property_name++)
    {
        char *property_value = NULL;
        error_code = (*jvmti_env)->GetSystemProperty(jvmti_env, *property_name, &property_value);
        check_jvmti_error(jvmti_env, error_code, "GetSystemProperty");
        printf("%-30s\t%s\n", *property_name, property_value);
        error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
        check_jvmti_error(jvmti_env, error_code, "Deallocate");
    }
    MSG("--- --- --- --- --- --- ---");
}
 
/*
 * Zmena jedne vybrane promenne.
 */
static void change_selected_property(jvmtiEnv *jvmti_env)
{
    jvmtiError error_code;
    error_code = (*jvmti_env)->SetSystemProperty(jvmti_env, "java.class.path", ".:new_path");
    check_jvmti_error(jvmti_env, error_code, "SetSystemProperty");
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
 
    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");
 
    /* Vytiskneme vybrane systemove promenne. */
    /* potom jednu zmenime a provede se novy tisk. */
    print_selected_system_properties(jvmti);
    change_selected_property(jvmti);
    print_selected_system_properties(jvmti);
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}

Opět můžeme provést test s využitím následujícího javovského programu:

/**
 * Vypise na standardni vystup hodnotu systemove promenne "java.class.path"
 */
public class Test8 {
 
    public static void main(String[] args) {
        System.out.println("--- Java side ---");
        System.out.format("%-30s\t%s\n", "java.class.path", System.getProperty("java.class.path"));
        System.out.println("--- Java side ---");
    }
 
}

Překlad:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent8.so agent8.c
javac Test8.java

Spuštění:

java -agentpath:./libagent8.so Test8

Ve výpise (tj. v zachyceném standardním výstupu) můžeme vidět, že javovský program skutečně získal modifikovanou hodnotu systémové proměnné java.class.path. Povšimněte si především zvýrazněných řádků:

Agent8: Agent_OnLoad
Agent8: JVM TI version is correct
Agent8: ---  System properties  ---
sun.java.command                Test8
java.home                       /usr/lib/jvm/java-6-openjdk/jre
java.class.path                 .
java.library.path               /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib
sun.boot.class.path             /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes
sun.boot.library.path           /usr/lib/jvm/java-6-openjdk/jre/lib/i386
java.ext.dirs                   /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
java.endorsed.dirs              /usr/lib/jvm/java-6-openjdk/jre/lib/endorsed
java.vm.version                 20.0-b12
java.vm.name                    OpenJDK Client VM
java.vm.info                    mixed mode, sharing
java.vm.vendor                  Sun Microsystems Inc.
Agent8: --- --- --- --- --- --- ---
Agent8: ---  System properties  ---
sun.java.command                Test8
java.home                       /usr/lib/jvm/java-6-openjdk/jre
java.class.path                 .:new_path
java.library.path               /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib
sun.boot.class.path             /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes
sun.boot.library.path           /usr/lib/jvm/java-6-openjdk/jre/lib/i386
java.ext.dirs                   /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
java.endorsed.dirs              /usr/lib/jvm/java-6-openjdk/jre/lib/endorsed
java.vm.version                 20.0-b12
java.vm.name                    OpenJDK Client VM
java.vm.info                    mixed mode, sharing
java.vm.vendor                  Sun Microsystems Inc.
--- Java side ---
java.class.path                 .:new_path
--- Java side ---
Agent8: --- --- --- --- --- --- ---
Agent8: Agent_OnUnload

6. Devátý demonstrační příklad: pokus o změnu nemodifikovatelné proměnné

Ve čtvrté kapitole jsme si řekli, že zdaleka ne všechny systémové proměnné je možné měnit. U některých implementací JVM například není možné modifikovat proměnnou java.vm.vendor, v níž je „podepsán“ dodavatel virtuálního stroje. Pokud se o změnu této proměnné pokusíme, dojde k chybě, což konkrétně znamená, že funkce SetSystemProperty() vrátí hodnotu JVMTI_ERROR_NOT_AVAILABLE a systémová proměnná se podle očekávání nezmění:

/*
 * Pokus o zmenu jedne vybrane promenne.
 */
static void change_selected_property(jvmtiEnv *jvmti_env)
{
    jvmtiError error_code;
    error_code = (*jvmti_env)->SetSystemProperty(jvmti_env, "java.vm.vendor", "Planet Express, Universe A");
    check_jvmti_error(jvmti_env, error_code, "SetSystemProperty");
}

Zdrojový kód celého demonstračního příkladu vypadá takto:

/*
 * JVM TI agent, ktery na standardni vystup vypise hodnoty
 * nekterych vybranych systemovych promennych, posleze
 * se pokusi zmenit jednu promennou.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent9:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Tisk nekterych vybranych systemovych promennych.
 */
static void print_selected_system_properties(jvmtiEnv *jvmti_env)
{
    /* pole se jmeny promennych - nutno ukoncit NULLem! */
    static char* property_names[] =
    {
        "sun.java.command",
        "java.home",
        "java.class.path",
        "java.library.path",
        "sun.boot.class.path",
        "sun.boot.library.path",
        "java.ext.dirs",
        "java.endorsed.dirs",
        "java.vm.version",
        "java.vm.name",
        "java.vm.info",
        "java.vm.vendor",
        NULL
    };
 
    jvmtiError error_code;
 
    MSG("---  System properties  ---");
 
    /* ziskame a vypiseme hodnoty vybranych systemovych promennych */
    char **property_name = property_names;
    for (; *property_name != NULL; property_name++)
    {
        char *property_value = NULL;
        error_code = (*jvmti_env)->GetSystemProperty(jvmti_env, *property_name, &property_value);
        check_jvmti_error(jvmti_env, error_code, "GetSystemProperty");
        printf("%-30s\t%s\n", *property_name, property_value);
        error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
        check_jvmti_error(jvmti_env, error_code, "Deallocate");
    }
    MSG("--- --- --- --- --- --- ---");
}
 
/*
 * Zmena jedne vybrane promenne.
 */
static void change_selected_property(jvmtiEnv *jvmti_env)
{
    jvmtiError error_code;
    error_code = (*jvmti_env)->SetSystemProperty(jvmti_env, "java.vm.vendor", "Planet Express, Universe A");
    check_jvmti_error(jvmti_env, error_code, "SetSystemProperty");
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
 
    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");
 
    /* Vytiskneme vybrane systemove promenne. */
    /* potom jednu zmenime a provede se novy tisk. */
    print_selected_system_properties(jvmti);
    change_selected_property(jvmti);
    print_selected_system_properties(jvmti);
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}

Překlad:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent9.so agent9.c
javac Test9.java

Spuštění:

java -agentpath:./libagent9.so Test9

Na standardním a chybovém výstupu je vypsána i chyba funkce SetSystemProperty():

Agent9: Agent_OnLoad
Agent9: JVM TI version is correct
Agent9: ---  System properties  ---
sun.java.command                Test9
java.home                       /usr/lib/jvm/java-6-openjdk/jre
java.class.path                 .
java.library.path               /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib
sun.boot.class.path             /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes
sun.boot.library.path           /usr/lib/jvm/java-6-openjdk/jre/lib/i386
java.ext.dirs                   /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
java.endorsed.dirs              /usr/lib/jvm/java-6-openjdk/jre/lib/endorsed
java.vm.version                 20.0-b12
java.vm.name                    OpenJDK Client VM
java.vm.info                    mixed mode, sharing
java.vm.vendor                  Sun Microsystems Inc.
Agent9: --- --- --- --- --- --- ---
Agent9: ERROR: JVMTI: 98(JVMTI_ERROR_NOT_AVAILABLE): SetSystemProperty
Agent9: ---  System properties  ---
sun.java.command                Test9
java.home                       /usr/lib/jvm/java-6-openjdk/jre
java.class.path                 .
java.library.path               /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib
sun.boot.class.path             /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes
sun.boot.library.path           /usr/lib/jvm/java-6-openjdk/jre/lib/i386
java.ext.dirs                   /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
java.endorsed.dirs              /usr/lib/jvm/java-6-openjdk/jre/lib/endorsed
java.vm.version                 20.0-b12
java.vm.name                    OpenJDK Client VM
java.vm.info                    mixed mode, sharing
java.vm.vendor                  Sun Microsystems Inc.
--- Java side ---
java.vm.vendor                  Sun Microsystems Inc.
--- Java side ---
Agent9: --- --- --- --- --- --- ---
Agent9: Agent_OnUnload

7. Funkce GetSystemProperties() určená pro čtení názvů všech systémových proměnných

Všechny demonstrační příklady ukázané v předchozích kapitolách byly založeny na znalosti správného názvu systémových proměnných. Ovšem rozhraní JVM TI obsahuje i funkci nazvanou GetSystemProperties(), kterou je možné použít pro získání jmen (nikoli hodnot!) všech systémových proměnných. Podobná funkce (metoda) existuje i v Javě, ovšem v případě rozhraní JVM TI je vše komplikováno tím, že se programátor musí postarat o dealokaci paměti obsahující vrácená jména systémových proměnných:

jvmtiError GetSystemProperties(
        jvmtiEnv* env,
        jint* count_ptr,
        char*** property_ptr)

Tato funkce ve svém druhém parametru vrátí počet všech systémových proměnných a ve svém třetím parametru pak pole řetězců s názvy těchto proměnných. Pole se musí interně vytvořit, proto se předává ukazatel na toto pole, tj. char***, protože char** značí pole řetězců a char* jeden řetězec. S těmito názvy již umíme pracovat – použijeme je pro získání hodnoty proměnné s využitím funkce GetSystemProperty():

    char **property_names;
    jint count;
    int i;
 
    error_code = (*jvmti_env)->GetSystemProperties(jvmti_env, &count, &property_names);
 
    /* ziskame a vypiseme hodnoty vsech systemovych promennych */
    for (i = 0; i < count; i++)
    {
        char *prop_name = property_names[i];
        char *property_value = NULL;
        (*jvmti_env)->GetSystemProperty(jvmti_env, prop_name, &property_value);
        printf("%-30s\t%s\n", prop_name, property_value);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
    }

Velmi důležité je však uvolnit paměť alokovanou uvnitř funkce GetSystemProperties(). Jak již víme, jedná se o pole řetězců, takže dealokace probíhá ve dvou fázích. Ve fázi první se uvolní jednotlivé řetězce a teprve ve fázi druhé se uvolní celé pole (to obsahuje pouze ukazatele):

    /* faze dealokace pameti */
    for (i = 0; i < count; i++)
    {
        char *prop_name = property_names[i];
        printf("Deallocation of string '%-30s' with length %3d stored at %p\n",
                prop_name, strlen(prop_name), prop_name);
        (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)prop_name);
    }
    printf("Deallocation of an array stored at %p\n", property_names);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)property_names);

8. Desátý demonstrační příklad: výpis všech systémových proměnných nastavených virtuálním strojem

Výše uvedené dva fragmenty kódu můžeme snadno přidat do našeho testovacího agenta, v němž se nově objevuje funkce nazvaná print_all_system_properties():

/*
 * JVM TI agent, ktery na standardni vystup vypise hodnoty
 * vsech systemovych promennych.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent10:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Tisk vsech systemovych promennych.
 */
static void print_all_system_properties(jvmtiEnv *jvmti_env)
{
    jvmtiError error_code;
    jint count;
    char **property_names;
    int i;
 
    MSG("---  All system properties  ---");
 
    error_code = (*jvmti_env)->GetSystemProperties(jvmti_env, &count, &property_names);
 
    /* ziskame a vypiseme hodnoty vsech systemovych promennych */
    for (i = 0; i < count; i++)
    {
        char *prop_name = property_names[i];
        char *property_value = NULL;
        error_code = (*jvmti_env)->GetSystemProperty(jvmti_env, prop_name, &property_value);
        check_jvmti_error(jvmti_env, error_code, "GetSystemProperty");
        printf("%-30s\t%s\n", prop_name, property_value);
        error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char*) property_value);
        check_jvmti_error(jvmti_env, error_code, "Deallocate");
    }
    MSG("--- --- --- --- --- --- --- ---");
 
    /* faze dealokace pameti */
    for (i = 0; i < count; i++)
    {
        char *prop_name = property_names[i];
        printf("Deallocation of string '%-30s' with length %3d stored at %p\n",
                prop_name, strlen(prop_name), prop_name);
        error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)prop_name);
        check_jvmti_error(jvmti_env, error_code, "Deallocate");
    }
    printf("Deallocation of an array stored at %p\n", property_names);
    error_code = (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)property_names);
    check_jvmti_error(jvmti_env, error_code, "Deallocate");
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
 
    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");
 
    /* Vytiskneme vsechny systemove promenne. */
    print_all_system_properties(jvmti);
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}

Javovská část testu je velmi jednoduchá:

public class Test10 {
 
    public static void main(String[] args) {
    }
 
}

Překlad:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent10.so agent10.c
javac Test10.java

Spuštění:

java -agentpath:./libagent10.so Test10

Po spuštění se na standardní výstup skutečně vypíše obsah všech dostupných systémových proměnných:

Agent10: Agent_OnLoad
Agent10: JVM TI version is correct
Agent10: ---  All system properties  ---
java.vm.specification.name      Java Virtual Machine Specification
java.vm.version                 20.0-b12
java.vm.name                    OpenJDK Client VM
java.vm.info                    mixed mode, sharing
java.ext.dirs                   /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
java.endorsed.dirs              /usr/lib/jvm/java-6-openjdk/jre/lib/endorsed
sun.boot.library.path           /usr/lib/jvm/java-6-openjdk/jre/lib/i386
java.library.path               /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib
java.home                       /usr/lib/jvm/java-6-openjdk/jre
java.class.path                 .
sun.boot.class.path             /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes
java.vm.specification.vendor    Sun Microsystems Inc.
java.vm.specification.version   1.0
java.vm.vendor                  Sun Microsystems Inc.
sun.java.command                Test10
sun.java.launcher               SUN_STANDARD
Agent10: --- --- --- --- --- --- --- ---

Následně se vypíše zpráva o dealokaci paměti, přesněji řečeno pole řetězců s názvy systémových proměnných:

Deallocation of string 'java.vm.specification.name    ' with length  26 stored at 0x8cb7d40
Deallocation of string 'java.vm.version               ' with length  15 stored at 0x8cb7d60
Deallocation of string 'java.vm.name                  ' with length  12 stored at 0x8cb7d78
Deallocation of string 'java.vm.info                  ' with length  12 stored at 0x8cb7d90
Deallocation of string 'java.ext.dirs                 ' with length  13 stored at 0x8cb7da8
Deallocation of string 'java.endorsed.dirs            ' with length  18 stored at 0x8cb7dc0
Deallocation of string 'sun.boot.library.path         ' with length  21 stored at 0x8cb7dd8
Deallocation of string 'java.library.path             ' with length  17 stored at 0x8cb7df8
Deallocation of string 'java.home                     ' with length   9 stored at 0x8cb7e10
Deallocation of string 'java.class.path               ' with length  15 stored at 0x8cb7e20
Deallocation of string 'sun.boot.class.path           ' with length  19 stored at 0x8cb7e38
Deallocation of string 'java.vm.specification.vendor  ' with length  28 stored at 0x8cb7e50
Deallocation of string 'java.vm.specification.version ' with length  29 stored at 0x8cb7e78
Deallocation of string 'java.vm.vendor                ' with length  14 stored at 0x8cb7ea0
Deallocation of string 'sun.java.command              ' with length  16 stored at 0x8cb7eb8
Deallocation of string 'sun.java.launcher             ' with length  17 stored at 0x8cb7ed0
Deallocation of an array stored at 0x8cb7cf8
Agent10: Agent_OnUnload

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?
Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Tesco: Chudá rodina si koupí levné polské kuře

Tesco: Chudá rodina si koupí levné polské kuře

Vitalia.cz: To nejhorší při horečce u dětí: Febrilní křeče

To nejhorší při horečce u dětí: Febrilní křeče

Lupa.cz: Na koho se v Křišťálové Lupě nedostalo?

Na koho se v Křišťálové Lupě nedostalo?

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Často čůrá a má žízeň? Příznaky dětské cukrovky

Často čůrá a má žízeň? Příznaky dětské cukrovky

Lupa.cz: Seznam mění vedení. Pavel Zima v čele končí

Seznam mění vedení. Pavel Zima v čele končí

DigiZone.cz: Sony KD-55XD8005 s Android 6.0

Sony KD-55XD8005 s Android 6.0

Podnikatel.cz: Babiš: E-shopy z EET možná vyjmeme

Babiš: E-shopy z EET možná vyjmeme

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

Podnikatel.cz: Babiše přesvědčila 89letá podnikatelka?!

Babiše přesvědčila 89letá podnikatelka?!

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

Vitalia.cz: Chtějí si léčit kvasinky. Lék je jen v Německu

Chtějí si léčit kvasinky. Lék je jen v Německu

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

Lupa.cz: UX přestává pro firmy být magie

UX přestává pro firmy být magie

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?