Hlavní navigace

Pohled pod kapotu JVM – spuštění a monitorování virtuálního stroje Javy s využitím rozhraní JDI

Pavel Tišnovský 2. 4. 2013

V dnešní části seriálu o programovacím jazyku Java i o virtuálním stroji Javy se již podruhé budeme zabývat rozhraním JDI (Java Debugger Interface). Ukážeme si, jak lze přes JDI spustit a následně i sledovat JVM a především aplikace spuštěné v tomto virtuálním stroji – stejným způsobem pracují i debuggery.

Obsah

1. Krátké shrnutí předchozí části seriálu: konektory v rozhraní JDI

2. Tři typy konektorů nabízených rozhraním JDI

3. Základ JDI: třída com.sun.jdi.Bootstrap a rozhraní com.sun.jdi.VirtualMachineManager

4. Metody předepsané rozhraním com.sun.jdi.connect.Launchin­gConnector

5. První demonstrační příklad – výpis všech argumentů výchozího konektoru typu LaunchingConnector

6. Spuštění nového virtuálního stroje Javy s využitím výchozího konektoru typu LaunchingConnector

7. Specifikace třídy, jejíž metoda main() se má v nově vytvořeném virtuálním stroji spustit

8. Přečtení standardního výstupu spuštěného virtuálního stroje Javy

9. Druhý demonstrační příklad – spuštění nové JVM s přečtením výstupu testovací aplikace

10. Odkazy na Internetu

1. Krátké shrnutí předchozí části seriálu: konektory v rozhraní JDI

V dnešní části seriálu o programovacím jazyku Java i o virtuálním stroji Javy bude pokračovat v popisu javovského rozhraní JDI (Java Debugger Interface), které slouží, jak již název tohoto rozhraní napovídá, k implementaci různých ladicích nástrojů spolupracujících s virtuálním strojem Javy i s aplikacemi, které jsou v rámci tohoto virtuálního stroje spuštěny. Minule jsme si řekli základní informace o tomto rozhraní i o začlenění JDI do architektury nazvané JPDA (Java Platform Debugger Architecture). Připomeňme si, že JDI vývojářům debuggerů nabízí hned několik možností propojení mezi debuggerem a cílovým JVM, na němž je spuštěna laděná aplikace. O propojení mezi JDI a cílovou JVM se z hlediska programátora starají takzvané „konektory“, kterých existuje hned několik typů v závislosti na tom, jakým způsobem je debugger přes JNI k cílové JVM připojován:

# Konektor Plné jméno (identifikace) Popis
1 CommandLineLaunch „com.sun.jdi.CommandLineLaunch“ cílová JVM je spuštěna přímo přes JDI a ihned poté se naváže spojení přes socket či sdílenou paměť
2 RawCommandLineLaunch „com.sun.jdi.RawCommandLineLaunch“ podobné předchozímu konektoru, ovšem příkaz pro spuštění JVM se předává přes jediný řetězec (liší se jen způsob spuštění)
3 SocketAttach „com.sun.jdi.SocketAttach“ připojení k již běžící cílové JVM přes socket
4 SharedMemoryAttach „com.sun.jdi.SharedMemoryAttach“ připojení k již běžící cílové JVM přes sdílenou paměť
5 SocketListen „com.sun.jdi.SocketListen“ připojení k běžící cílové JVM na základě požadavku přijatého od této JVM
6 SharedMemoryListen „com.sun.jdi.SharedMemoryListen“ dtto, ale využije se sdílená paměť
7 ProcessAttach „com.sun.jdi.ProcessAttach“ připojení k již běžící cílové JVM, která je spuštěna s parametrem agentlib:jdwp=server=y

Obrázek 1: Vzájemný vztah mezi již popsaným (céčkovým) rozhraním JVM TI, protokolem JDWP a javovským rozhraním JDI. JVM TI, JDWP i JDI jsou součástí architektury JPDA – Java Platform Debugger Architecture.

2. Tři typy konektorů nabízených rozhraním JDI

Ve skutečnosti může na některých počítačových architekturách existovat i větší množství konektorů, takže tabulka uvedená v předchozí kapitole není úplná. Ovšem důležité je, že nezávisle na tom, kolik konektorů na dané počítačové architektuře existuje, je vždy můžeme rozdělit do tří skupin. První skupina konektorů implementuje rozhraní LaunchingConnector a je typická tím, že konektor sám spustí cílovou JVM i s laděnou aplikací. Typicky se jedná o konektor velmi často používaný různými integrovanými vývojovými prostředími pro spuštění a následné ladění aplikace. Druhá skupina konektorů implementuje rozhraní AttachingConnector. Jedná se o konektory, které se dokážou připojit k již běžící JVM. Tato JVM musí být spuštěna s volbou:

agentlib:jdwp=transport=typ_konektoru,server=y

Třetí typ konektorů implementuje rozhraní ListeningConnector. Tyto konektory slouží k tomu, aby se cílová JVM sama připojila k debuggeru, který již musí být spuštěn. Cílový virtuální stroj Javy bývá v tomto případě spuštěn s volbou:

agentlib:jdwp=transport=typ_konektoru,address=zvolena_adresa

Kde se namísto řetězce zvolena adresa zadá skutečná adresa, na které „poslouchá“ debugger.

Všechny tři skupiny konektorů jsou pro větší přehled vypsány v další tabulce i s příslušnými implementacemi:

# Typ konektoru Metoda propojení debuggeru a JVM Implementace
1 LaunchingConnector debugger spouští JVM com.sun.jdi.CommandLineLaunch, com.sun.jdi.RawCommandLineLaunch
2 AttachingConnector debugger se připojuje k běžící JVM com.sun.jdi.SocketAttach, com.sun.jdi.SharedMemoryAttach, com.sun.jdi.ProcessAttach
3 ListeningConnector debugger akceptuje žádost JVM o připojení com.sun.jdi.SocketListen, com.sun.jdi.SharedMemoryListen

Obrázek 2: (zopakování z minula) s využitím protokolu JDWP může s běžící (cílovou) JVM komunikovat prakticky libovolný proces; nemusí se tedy ve všech případech jednat o aplikaci naprogramovanou v Javě.

3. Základ JDI: třída com.sun.jdi.Bootstrap a rozhraní com.sun.jdi.VirtualMachineManager

Při použití rozhraní JDI se hned v počátečních fázích vývoje jednoduchého debuggeru setkáme s třídou nazvanou com.sun.jdi.Bootstrap a s rozhraním com.sun.jdi.Interface VirtualMachineManager (v názvech používám i jména balíčků, protože se NEjedná o třídy a rozhraní ze standardního Java SE API). Třída com.sun.jdi.Bootstrap je ve skutečnosti velmi jednoduchá, protože obsahuje pouze jednu metodu nazvanou virtualMachineManager. Tato metoda vrací jedinou instanci (singleton) třídy implementující rozhraní com.sun.jdi.Interface VirtualMachineManager (pozor, existuje větší množství tříd se stejným jménem, ovšem ležící v jiném balíčku):

com.sun.jdi.Bootstrap
public static VirtualMachineManager virtualMachineManager()

Rozhraní com.sun.jdi.Interface VirtualMachineManager je již pro vývojáře zajímavější, protože třídy, které toto rozhraní implementují, musí obsahovat mj. i následujících deset metod:

# Metoda Popis
1 int majorInterfaceVersion() vrací majoritní číslo verze rozhraní JDI
2 int minorInterfaceVersion() vrací minoritní číslo verze rozhraní JDI
     
3 LaunchingConnector defaultConnector() vrací výchozí konektor typu LaunchingConnector
4 List<LaunchingConnector> launchingConnectors() vrací všechny dostupné (implementované) konektory typu LaunchingConnector
5 List<ListeningConnector> listeningConnectors() vrací všechny dostupné (implementované) konektory typu ListeningConnector
6 List<AttachingConnector> attachingConnectors() vrací všechny dostupné (implementované) konektory typu AttachingConnector
7 List<Connector> allConnectors() vrací všechny dostupné (implementované) konektory – kombinace předchozích metod
     
8 VirtualMachine createVirtualMachine(Connection connection) vytvoření nové instance virtuálního stroje
9 VirtualMachine createVirtualMachine(Connection connection, Process process) vytvoření nové instance virtuálního stroje
10 List<VirtualMachine>connec­tedVirtualMachines() vrací seznam všech JVM připojených k debuggeru

Tyto metody lze rozdělit do tří skupin. Metody z první skupiny jsou velmi jednoduché, protože pouze slouží k vrácení majoritního a minoritního čísla verze rozhraní JDI. Ve druhé skupině se nachází metody vracející instanci jednoho konektoru, popř. seznam všech konektorů daného typu; ostatně s metodou allConnectors() jsme se již setkali minule. Ve třetí skupině metod najdeme metody sloužící ke spuštění nového virtuálního stroje Javy, popř. pro zjištění všech takto spuštěných JVM. Tyto metody se většinou nepoužívají přímo, protože pro spuštění virtuálního stroje se používá spíše metoda Connector.launch(), kterou si popíšeme v dalším textu.

4. Metody předepsané rozhraním com.sun.jdi.connect.Launchin­gConnector

Nyní si ukažme, jakým způsobem můžeme využít metodu com.sun.jdi.VirtualMachine­Manager.defaultConnector(). Z tabulky uvedené v předchozí kapitole již víme, že tato metoda vrátí konektor typu LaunchingConnector, což znamená konektor, který je schopen vytvořit nový virtuální stroj Javy a určit, která třída v nově vytvořeném JVM bude spuštěna (tato třída musí obsahovat statickou metodu main). LaunchingConnector je ve skutečnosti rozhraní rozšiřující jiné rozhraní nazvané jednoduše Connector:

com.sun.jdi.connect
public interface LaunchingConnector extends Connector

V rozhraní LaunchingConnector nalezneme předpis pro několik zajímavých metod, které jsou vypsány v tabulce níže:

# Metoda Popis
1 String name() vrátí řetězcový identifikátor konektoru
2 String description() vrátí řetězec s popisem konektoru v čitelné podobě
3 Transport transport() určeno pro zjištění transportního mechanismu použitého pro napojení na cílovou (laděnou) JVM
4 VirtualMachine launch(Map<String, ? extends Connector.Argument> arguments) spuštění cílové JVM s laděnou aplikací
5 Map<String,Connector.Argument> defaultArguments() vrátí argumenty použité konektorem společně s výchozími hodnotami těchto argumentů

Jak tento konektor spouští nový cílový virtuální stroj Javy se dozvíme až v následujících kapitolách, nyní nás bude zajímat především poslední metoda nazvaná defaultArguments(). Tato metoda vrátí mapu (asociativní pole), v níž jsou jako klíče použita jména argumentů a hodnoty jsou typu Connector.Argument. Důležité je, že tuto mapu není možné rozšiřovat, přesněji řečeno není možné rozšířenou mapu použít v metodě launch(). Můžeme ovšem měnit hodnoty jednotlivých argumentů, což je velmi důležité, protože v jednom argumentu je uložen i název třídy, která se má v nově vytvořeném virtuálním stroji Javy spustit.

5. První demonstrační příklad – výpis všech argumentů výchozího konektoru typu LaunchingConnector

V dnešním prvním demonstračním příkladu je ukázáno, jak lze využít metodu Bootstrap.virtualMachineManager() pro získání instance třídy implementující rozhraní VirtualMachineManager. Následně je zavolána metoda VirtualMachineManager.defau­ltConnector() vracející výchozí konektor dostupný v prakticky každé implementaci JRE. Tato metoda vrací instanci třídy implementující rozhraní LaunchingConnector. Jakmile jsme získali tento objekt, můžeme přes metodu Connector.defaultArguments() získat mapu obsahující jména i hodnoty všech argumentů výchozího konektoru. Tyto informace jsou následně vypsány na standardní výstup:

import java.util.Map;
 
import com.sun.jdi.Bootstrap;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.LaunchingConnector;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.Connector.Argument;
 
/**
 * Trida, ktera vypise vsechny argumenty
 * vychoziho launching connectoru
 * (presneji receno konektoru typu LaunchingConnector).
 *
 * @author Pavel Tisnovsky
 */
public class JDIListLaunchingConnectorArguments {
 
    public static void main(String[] args) {
        // ziskat (jedinou) instanci tridy implementujici rozhrani VirtualMachineManager
        VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();
 
        // ziskat vychozi launching connector
        LaunchingConnector connector = virtualMachineManager.defaultConnector();
 
        // ziskat a vypsat vsechny parametry launching connectoru
        // (kazdy argument je typu com.sun.jdi.connect.Connector.Argument)
        Map<String, Argument> arguments = connector.defaultArguments();
        for (Map.Entry<String, Connector.Argument> argument : arguments.entrySet()) {
            System.out.println(argument.getKey() + "\t" + argument.getValue());
        }
    }
 
}

Překlad prvního demonstračního příkladu se provede následovně:

javac -classpath /usr/lib/jvm/java-6-openjdk/lib/tools.jar:. JDIListLaunchingConnectorArguments.java

Spuštění zajistí příkaz:

java -cp /usr/lib/jvm/java-6-openjdk/lib/tools.jar:. JDIListLaunchingConnectorArguments

(v obou případech je nutné udat cestu k javovskému archivu tools.jar)

Příklad výpisu výchozích parametrů po spuštění na Linuxu:

home    home=/usr/lib/jvm/java-6-openjdk/jre
options options=
main    main=
suspend suspend=true
quote   quote="
vmexec  vmexec=java

Příklad výpisu výchozích parametrů po spuštění na MS Windows:

home    home=C:\Program Files\Java\jdk1.6.0_01\jre
options options=
main    main=
suspend suspend=true
quote   quote="
vmexec  vmexec=java

6. Spuštění nového virtuálního stroje Javy s využitím výchozího konektoru typu LaunchingConnector

V následujících čtyřech kapitolách si vysvětlíme, jakým způsobem lze argumenty výchozího konektoru typu LaunchingConnector modifikovat takovým způsobem, abychom konektor „donutili“ ke spuštění virtuálního stroje Javy i se specifikovanou třídou obsahující metodu main. Začneme jednoduše – získáním instance třídy implementující rozhraní LaunchingConnector. To pro nás není nic nového, protože stejný kód byl využit i v prvním demonstračním příkladu. Jakmile kýžený objekt získáme, zavolá se uživatelská metoda nazvaná launchVirtualMachine(), které se předá jak získaný objekt s konektorem, tak i jméno třídy, která se má spustit v nově vytvářené JVM:

        // ziskat (jedinou) instanci tridy implementujici rozhrani VirtualMachineManager
        VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();
 
        // ziskat vychozi launching connector
        LaunchingConnector connector = virtualMachineManager.defaultConnector();
 
        // spustit virtualni stroj Javy
        launchVirtualMachine(connector, "Test");

7. Specifikace třídy, jejíž metoda main() se má v nově vytvořeném virtuálním stroji spustit

V uživatelské metodě launchVirtualMachine() je nejprve nutné změnit výchozí argumenty konektoru takovým způsobem, aby se v nově vytvářeném virtuálním stroji Javy spouštěla vybraná třída. Tuto funkci zajistí následující kód:

        Map<String, Connector.Argument> arguments = connector.defaultArguments();
        arguments.get("main").setValue(main);

Z předchozího textu víme, že do mapy vrácené metodou Connector.defaultArguments() sice není možné přidávat další prvky ani není možné prvky odebírat, můžeme však měnit hodnoty jednotlivých argumentů. Nás zajímá argument uložený pod klíčem „main“, jemuž s využitím metody com.sun.jdi.connect.Connec­tor.Argument.setValue() změníme výchozí hodnotu "" na jméno konkrétní třídy, která se má skutečně spustit.

Dále se cílový virtuální stroj Javy skutečně spustí, i když výchozím stavem JVM bude pozastavení – suspend). Spuštění může vypadat takto:

        try {
            System.out.println("Starting virtual machine");
            VirtualMachine virtualMachine = connector.launch(arguments);
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (IllegalConnectorArgumentsException e) {
            e.printStackTrace();
        }
        catch (VMStartException e) {
            e.printStackTrace();
        }
    }

Kompletní tělo uživatelské metody launchVirtualMachine() je vypsáno pod tímto odstavcem:

    /**
     * Spusteni noveho virtualniho stroje Javy.
     *
     * @param connector instance launching connectoru
     * @param main jmeno tridy, ktera se ma v nove JVM spustit
     */
    private static void launchVirtualMachine(LaunchingConnector connector, String main) {
        // zmena hodnoty argumentu se jmenem "main"
        Map<String, Connector.Argument> arguments = connector.defaultArguments();
        arguments.get("main").setValue(main);
 
        // spusteni virtualniho stroje Javy
        try {
            System.out.println("Starting virtual machine");
            VirtualMachine virtualMachine = connector.launch(arguments);
            // precteni standardniho vystupu spustene JVM
            processVMStandardOutput(virtualMachine);
            System.out.println("Virtual machine stopped");
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (IllegalConnectorArgumentsException e) {
            e.printStackTrace();
        }
        catch (VMStartException e) {
            e.printStackTrace();
        }
    }

8. Přečtení standardního výstupu spuštěného virtuálního stroje Javy

Zbývají nám vyřešit dva problémy, které spolu úzce souvisí. První problém spočívá v tom, že je většinou nutné, aby debugger přečetl standardní a většinou i chybový výstup aplikace běžící v cílovém JVM. Pokud by k průběžnému čtení nedocházelo, byla by laděná aplikace běžící v cílovém JVM pozastavena ve chvíli, kdy by se naplnily vyrovnávací buffery standardního a chybového výstupu. Tento problém vyřešíme tak, že získáme objekt typu Process, který reprezentuje proces cílové JVM. Následně lze získat standardní/chybový výstup tohoto procesu s využitím metod Process.getInputStream() a Process.getErrorStrem(). Pojmenování těchto metod je poněkud matoucí, ale při bližším pohledu pochopitelné, protože standardní/chybový výstup jiného procesu je pro současný proces vstupním proudem, nikoli proudem výstupním. Standardní výstup získáme takto:

        Process process = virtualMachine.process();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

Jakmile je standardní výstup přesměrován, lze cílovou JVM znovu „rozjet“ metodou VirtualMachine.resume:

        // zmena stavu suspend->resume
        virtualMachine.resume();

Následně je nutné pečlivě přečíst celý standardní výstup cílové JVM. Celý kód zajišťující spuštění cílové JVM s přečtením jejího standardního výstupu vypadá následovně:

    /**
     * Precteni standardniho vystupu spustene JVM
     *
     * @param virtualMachine nove spustena JVM
     * @throws IOException
     */
    private static void processVMStandardOutput(VirtualMachine virtualMachine) throws IOException {
        Process process = virtualMachine.process();
        // nenechte se zmast - standardni vystup jineho procesu
        // je z naseho pohledu vstupem!
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
 
        // zmena stavu suspend->resume
        virtualMachine.resume();
 
        // precist standardni vystup nove JVM
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println("Child VM output: " + line);
        }
        reader.close();
    }

9. Druhý demonstrační příklad – spuštění nové JVM s přečtením výstupu testovací aplikace

Nyní si již můžeme ukázat kompletní zdrojový kód druhého demonstračního příkladu, který vytvoří novou instanci virtuálního stroje Javy a v tomto JVM spustí třídu nazvanou jednoduše „Main“. Veškerý text zapisovaný na standardní výstup cílové JVM je zachycen:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
 
import com.sun.jdi.Bootstrap;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
import com.sun.jdi.connect.LaunchingConnector;
import com.sun.jdi.connect.VMStartException;
 
/**
 * Spusteni noveho virtualniho stroje Javy
 * a precteni jeho standardniho vystupu.
 *
 * @author Pavel Tisnovsky
 */
public class JDILaunchingConnectorTest {
 
    public static void main(String[] args) {
        // ziskat (jedinou) instanci tridy implementujici rozhrani VirtualMachineManager
        VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();
 
        // ziskat vychozi launching connector
        LaunchingConnector connector = virtualMachineManager.defaultConnector();
 
        // spustit virtualni stroj Javy
        launchVirtualMachine(connector, "Test");
    }
 
    /**
     * Spusteni noveho virtualniho stroje Javy.
     *
     * @param connector instance launching connectoru
     * @param main jmeno tridy, ktera se ma v nove JVM spustit
     */
    private static void launchVirtualMachine(LaunchingConnector connector, String main) {
        // zmena hodnoty argumentu se jmenem "main"
        Map<String, Connector.Argument> arguments = connector.defaultArguments();
        arguments.get("main").setValue(main);
 
        // spusteni virtualniho stroje Javy
        try {
            System.out.println("Starting virtual machine");
            VirtualMachine virtualMachine = connector.launch(arguments);
            // precteni standardniho vystupu spustene JVM
            processVMStandardOutput(virtualMachine);
            System.out.println("Virtual machine stopped");
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (IllegalConnectorArgumentsException e) {
            e.printStackTrace();
        }
        catch (VMStartException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * Precteni standardniho vystupu spustene JVM
     *
     * @param virtualMachine nove spustena JVM
     * @throws IOException
     */
    private static void processVMStandardOutput(VirtualMachine virtualMachine) throws IOException {
        Process process = virtualMachine.process();
        // nenechte se zmast - standardni vystup jineho procesu
        // je z naseho pohledu vstupem!
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
 
        // zmena stavu suspend->resume
        virtualMachine.resume();
 
        // precist standardni vystup nove JVM
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println("Child VM output: " + line);
        }
        reader.close();
    }
 
}

Samotná třída Test je velmi jednoduchá:

public class Test {
 
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("Hello world!");
        }
    }
 
}

Překlad druhého demonstračního příkladu zajistí příkaz:

javac -classpath /usr/lib/jvm/java-6-openjdk/lib/tools.jar:. JDILaunchingConnectorTest.java

Spuštění se provede příkazem:

java -cp /usr/lib/jvm/java-6-openjdk/lib/tools.jar:. JDILaunchingConnectorTest

Po spuštění by se měl na standardní výstup vypsat následující text:

Starting virtual machine
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Child VM output: Hello world!
Virtual machine stopped

10. Odkazy na Internetu

  1. Breakpoint (Wikipedia)
    http://cs.wikipedia.org/wi­ki/Breakpoint
  2. JVM Tool Interface Version 1.2 Documentation
    http://docs.oracle.com/ja­vase/7/docs/platform/jvmti/jvmti­.html
  3. JVM Tool Interface Version 1.2 Documentation – SetBreakpoint
    http://docs.oracle.com/ja­vase/6/docs/platform/jvmti/jvmti­.html#SetBreakpoint
  4. JVM Tool Interface Version 1.2 Documentation – ClearBreakpoint
    http://docs.oracle.com/ja­vase/6/docs/platform/jvmti/jvmti­.html#ClearBreakpoint
  5. JVM Tool Interface Version 1.2 Documentation – Breakpoint (callback funkce)
    http://docs.oracle.com/ja­vase/6/docs/platform/jvmti/jvmti­.html#Breakpoint
  6. The JVM Tool Interface (JVM TI): How VM Agents Work
    http://www.oracle.com/technet­work/articles/javase/jvm-ti-141370.html
  7. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  8. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  9. 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
  10. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  11. 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
  12. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  13. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  14. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  15. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  16. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  17. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  18. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  19. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
Našli jste v článku chybu?
Lupa.cz: Slevové šílenství je tu. Kde nakoupit na Black Friday?

Slevové šílenství je tu. Kde nakoupit na Black Friday?

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

Podnikatel.cz: Vládu obejde, kvůli EET rovnou do sněmovny

Vládu obejde, kvůli EET rovnou do sněmovny

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

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

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Vitalia.cz: Říká amoleta - a myslí palačinka

Říká amoleta - a myslí palačinka

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

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

Podnikatel.cz: Víme první výsledky doby odezvy #EET

Víme první výsledky doby odezvy #EET

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Lupa.cz: Avast po spojení s AVG propustí 700 lidí

Avast po spojení s AVG propustí 700 lidí

120na80.cz: Bojíte se encefalitidy?

Bojíte se encefalitidy?

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

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

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

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

120na80.cz: Jak oddálit Alzheimera?

Jak oddálit Alzheimera?

Vitalia.cz: Baletky propagují zdravotní superpostel

Baletky propagují zdravotní superpostel