Articles
Java Platform, Standard Edition
|
|
| By Kelly O'Hair, July 1, 2004 |
|
| - | Some History and Background |
| - | Transition Issues for JVMPI Agents |
| - | Some Basics on JVMTI Usage |
| - | Object Tagging and Heap Iteration |
| - | Summary |
| - | About the Author |
JNIEnv* argument to facilitate JNI usage. Multiple JVMTI agents can operate in a single JVM and all interfaces return an error code to determine success or failure of the request. It is the intention of JVMTI to displace both JVMPI and JVMDI, and to ultimately be the single native tool interface into the JVM.In general, you will find that JVMTI has many more interfaces than JVMPI. Here is a map from JVMPI to JVMTI, or some notes on why there is no mapping.
|
JVMPI
|
JVMTI
|
Notes
|
|
|---|---|---|---|
EnableEvent |
SetEventNotificationMode |
Use jvmtiEventMode == JVMTI_ENABLE |
|
DisableEvent |
SetEventNotificationMode |
Use jvmtiEventMode == JVMTI_DISABLE |
|
RequestEvent |
No JVMTI equivalent. This was used for creation of JVMPI Heap, Monitor, or Object event dumps, which are not created by JVMTI. JVMTI instead uses function calls to extract this information. JVMTI GenerateEvents is not the same thing, be careful. |
||
GetCallTrace |
GetStackTrace |
JVMTI provides bytecode offsets, not line numbers. | |
ProfilerExit |
No direct JVMTI equivalent. It wasn't clear that a special JVMTI Exit function was necessary. Use of the standard exit() function or JNI FatalError seemed to cover this. |
||
RawMonitorCreate |
CreateRawMonitor |
Basic raw monitor usage is the same, just a typedef name change: JVMPI_RawMonitor -> jrawMonitorID |
|
RawMonitorEnter |
RawMonitorEnter |
||
RawMonitorExit |
RawMonitorExit |
||
RawMonitorWait |
RawMonitorWait |
||
RawMonitorNotifyAll |
RawMonitorNotifyAll |
||
RawMonitorDestroy |
DestroyRawMonitor |
||
GetCurrentThreadCpuTime |
GetCurrentThreadCpuTime |
||
SuspendThread |
SuspendThread |
For all thread interfaces, JVMTI accepts a jthread, not a JNIEnv*, and NULL can be used as the current thread. |
|
SuspendThreadList |
SuspendThreadList |
||
ResumeThread |
ResumeThread |
||
ResumeThreadList |
ResumeThreadList |
||
GetThreadStatus |
GetThreadState |
The JVMTI thread state contains more detail on the thread state. | |
ThreadHasRun |
No direct JVMTI equivalent. Sampling the current frame location and the GetCurrentThreadCpuTime may provide a replacement for this feature. |
||
CreateSystemThread |
RunAgentThread |
JVMTI requires the caller to provide a freshly created java.lang.Thread object. |
|
SetThreadLocalStorage |
SetThreadLocalStorage |
||
GetThreadLocalStorage |
GetThreadLocalStorage |
||
DisableGC |
No JVMTI equivalent. JVMPI required GC to be disabled for some operations, JVMTI does not. | ||
EnableGC |
No JVMTI equivalent. JVMPI required GC to be disabled for some operations, JVMTI does not. | ||
RunGC |
ForceGarbageCollection |
||
GetThreadObject |
No JVMTI equivalent, not needed. JVMTI uses jobject not object IDs. | ||
GetMethodClass |
GetMethodDeclaringClass |
||
jobjectID2jobject |
No JVMTI equivalent, not needed. JVMTI uses jobject not object IDs. | ||
jobject2jobjectI |
No JVMTI equivalent, not needed. JVMTI uses jobject not object IDs. | ||
The JVMPI event callback mechanism consisted of one callback function that was passed a large C struct/union of information. In JVMTI, each event has its own callback function with a unique function prototype for that event.
|
JVMPI Event
|
JVMTI Event
|
Notes
|
|
|---|---|---|---|
JVMPI_EVENT_ARENA_DELETE |
No JVMTI equivalent. Never implemented in the Reference Implementation of JVMPI. Depends too much on the specific Garbage Collection being used. | ||
JVMPI_EVENT_ARENA_NEW |
No JVMTI equivalent. Never implemented in the Reference Implementation of JVMPI. Depends too much on the specific Garbage Collection being used. | ||
JVMPI_EVENT_CLASS_LOAD |
JVMTI_EVENT_CLASS_LOAD |
||
JVMPI_EVENT_CLASS_LOAD_HOOK |
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK |
||
JVMPI_EVENT_CLASS_UNLOAD |
No direct JVMTI equivalent. Class unloads can be detected by a query of all classes loaded, and comparing it to a previously saved list. Or can be detected by using SetTag and ObjectFree events. | ||
JVMPI_EVENT_COMPILED_METHOD_LOAD |
JVMTI_EVENT_COMPILED_METHOD_LOAD |
||
JVMPI_EVENT_COMPILED_METHOD_UNLOAD |
JVMTI_EVENT_COMPILED_METHOD_UNLOAD |
||
JVMPI_EVENT_DATA_DUMP_REQUEST |
JVMTI_EVENT_DATA_DUMP_REQUEST |
||
JVMPI_EVENT_DATA_RESET_REQUEST |
No JVMTI equivalent. In all known JVMPI implementations it was redundant with JVMPI_EVENT_DATA_DUMP_REQUEST. It was determined that this event was not necessary in JVMTI. |
||
JVMPI_EVENT_GC_FINISH |
JVMTI_EVENT_GARBAGE_COLLECTION_FINISH |
||
JVMPI_EVENT_GC_START |
JVMTI_EVENT_GARBAGE_COLLECTION_START |
||
JVMPI_EVENT_HEAP_DUMP |
No direct JVMTI equivalent. See JVMTI Heap Iterate. | ||
JVMPI_EVENT_JNI_GLOBALREF_ALLOC |
No direct JVMTI equivalent. See JVMTI SetJNIFunctionTable. |
||
JVMPI_EVENT_JNI_GLOBALREF_FREE |
No direct JVMTI equivalent. See JVMTI SetJNIFunctionTable. |
||
JVMPI_EVENT_JNI_WEAK_GLOBALREF_ALLOC |
No direct JVMTI equivalent. See JVMTI SetJNIFunctionTable. |
||
JVMPI_EVENT_JNI_WEAK_GLOBALREF_FREE |
No direct JVMTI equivalent. See JVMTI SetJNIFunctionTable. |
||
JVMPI_EVENT_JVM_INIT_DONE |
JVMTI_EVENT_VM_INIT |
||
JVMPI_EVENT_JVM_SHUT_DOWN |
JVMTI_EVENT_VM_DEATH |
||
JVMPI_EVENT_METHOD_ENTRY |
JVMTI_EVENT_METHOD_ENTRY |
JVMTI version not equivalent to JVMPI version and should not be used where performance is an issue. See BCI. | |
JVMPI_EVENT_METHOD_ENTRY2 |
No direct JVMTI equivalent. With JVMTI, you would need to use GetLocalVariable on "this". |
||
JVMPI_EVENT_METHOD_EXIT |
JVMTI_EVENT_METHOD_EXIT |
JVMTI version not equivalent to JVMPI version and should not be used where performance is an issue. See BCI. | |
JVMPI_EVENT_MONITOR_CONTENDED_ENTER |
JVMTI_EVENT_MONITOR_CONTENDED_ENTER |
||
JVMPI_EVENT_MONITOR_CONTENDED_ENTERED |
JVMTI_EVENT_MONITOR_CONTENDED_ENTERED |
||
JVMPI_EVENT_MONITOR_CONTENDED_EXIT |
No JVMTI equivalent. | ||
JVMPI_EVENT_MONITOR_DUMP |
No direct JVMTI equivalent. See JVMTI Thread and Monitor. | ||
JVMPI_EVENT_MONITOR_WAIT |
JVMTI_EVENT_MONITOR_WAIT |
||
JVMPI_EVENT_MONITOR_WAITED |
JVMTI_EVENT_MONITOR_WAITED |
||
JVMPI_EVENT_OBJECT_ALLOC |
No direct JVMTI equivalent. See BCI. | ||
JVMPI_EVENT_OBJECT_DUMP |
No direct JVMTI equivalent. See BCI. | ||
JVMPI_EVENT_OBJECT_FREE |
JVMTI_EVENT_OBJECT_FREE |
Requires that the object be tagged (see JVMTI SetTag) which likely requires BCI. |
|
JVMPI_EVENT_OBJECT_MOVE |
No JVMTI equivalent, not needed. The object type jobject does not move. |
||
JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTER |
No JVMTI equivalent. | ||
JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTERED |
No JVMTI equivalent. | ||
JVMPI_EVENT_RAW_MONITOR_CONTENDED_EXIT |
No JVMTI equivalent. | ||
JVMPI_EVENT_THREAD_END |
JVMTI_EVENT_THREAD_END |
||
JVMPI_EVENT_THREAD_START |
JVMTI_EVENT_THREAD_START |
||
JVMPI_EVENT_INSTRUCTION_START |
No direct JVMTI equivalent. See JVMTI_EVENT_SINGLE_STEP for a possible replacement. Also see BCI. |
||
Probably the most significant difference between JVMPI and JVMTI is the handling of the Java Heap. With JVMPI, a request would be made for a Heap Dump, and the resulting JVMPI event would contain a single large block of data in a specific format that would need to be parsed by the agent library. With JVMTI, there are some basic Heap Iteration functions to traverse the Heap in various general ways, but to track all object allocations in detail, the objects need to be captured at the allocation site and tagged. The tags are carried with the object and can be used in various ways. In some situations like the JVMTI_EVENT_OBJECT_FREE event, the object must be tagged to get the event. Tagging objects usually requires some degree of BCI, which JVMTI supports in various ways. BCI involves inserting additional bytecodes into methods to instrument the method. BCI can be accomplished by displacing the original classes through the CLASSPATH with the instrumented classes, changing the class image at class load time with the JVMTI_EVENT_CLASS_FILE_LOAD_HOOK event, or calling JVMTI RedefineClasses to displace a class image that was previously loaded. Note that JVMTI just provides the ability to do BCI, it does not provide BCI support code for you. See the demo/jvmti library java_crw_demo.
JVMPI_EVENT_OBJECT_ALLOC is not available in JVMTI. Use of BCI, SetTag, and the event JVMTI_EVENT_OBJECT_FREE can be used to obtain the same functionality.With JVMPI, the first agent library code to be executed was the JVM_OnLoad extern, with JVMTI it is the Agent_OnLoad extern. The initial JVMPI event is JVMPI_EVENT_JVM_INIT_DONE, for JVMTI the equivalent event is JVMTI_EVENT_VM_INIT. However, JVMTI also provides an earlier event, JVMTI_EVENT_VM_START, that represents the earliest possible time where a JNI call can be made. JVMTI has distinct phases (see jvmtiPhase) and all interfaces have specifications as to which phases they can be called in. With JVMTI, it is important that the needed JVMTI capabilities be added to the JVMTI environment ( jvmtiEnv*) during the on load phase (inside Agent_OnLoad). JVMTI also provides an equivalent Agent_OnUnload for when the agent library is actually unloaded from the VM process.
A special note here for people attempting to create a single shared library that is composed of both JVMPI or JVMDI usage along with JVMTI. A JVMTI agent library can be loaded into the JVM with the -agentlib or -agentpath options, but can also be loaded in with the older -Xrun option. The version 1.5.0 JVM, when given the -Xrun option, first looks for the Agent_OnLoad extern in the library, and if seen, gives JVMTI priority, only calling Agent_OnLoad and ignoring any JVM_OnLoad extern, effectively treating this -Xrun option usage as if it was an -agentlib option. Since the older JVM implementations only accept the -Xrun option, and will only be looking for the JVM_OnLoad extern, a single shared library containing both the Agent_OnLoad and the JVM_OnLoad externs could serve as double agents when using the -Xrun option, appearing as a JVMPI or JVMDI agent library to the older JVM implementations, and as a JVMTI agent library to the version 1.5.0 JVM implementation. Whether this is a good idea or not is left as an exercise to the reader.
In addition, another difference to note is that Agent_OnLoad will be called earlier in the JVM startup than JVM_OnLoad. The JVMTI phases protect you from calls happening too early inside Agent_OnLoad, but this could be an important difference for some users.
demo/jvmti directory).Agents are loaded into a JVM through one of the java options -agentlib: agent[= options] or –agentpath: path[= options] . In addition, the older java option –Xrun can be used (for example, –Xrun agent [:options]).
The Agent_OnLoad extern is the very first agent library code to be executed. Inside Agent_OnLoad the JVMTI environment is requested from the JVM, the necessary JVMTI capabilities are added to that environment, and the initial JVMTI event callbacks are setup and enabled. Some of the JVMTI setup can be done here, or in the JVMTI_EVENT_VM_INIT event callback, but parts of JVMTI cannot be called until the JVMTI_EVENT_VM_INIT event, so JVMTI calls inside Agent_OnLoad are limited to the "onload" phase (see the discussion of JVMTI phases below).
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jint rc; jvmtiCapabilities capabilities; jvmtiEventCallbacks callbacks; jvmtiEnv *jvmti; rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION); if (rc != JNI_OK) { fprintf(stderr, "ERROR: Unable to create jvmtiEnv, GetEnv failed, error=%d\n", rc); return -1; } (*jvmti)->GetCapabilities(jvmti, &capabilities); capabilities.can_tag_objects = 1; capabilities.can_generate_garbage_collection_events = 1; (*jvmti)->AddCapabilities(jvmti, &capabilities); memset(&callbacks, 0, sizeof(callbacks)); callbacks.VMInit = &vmInit; callbacks.VMDeath = &vmDeath; (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks)); (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL); return 0; } |
Note that all of the JVMTI error return handling and checking code has been left out of the above example. Do not do this in real life; those error returns are always worth checking.
The JVMTI capabilities are best added very early, depending on the implementation. The available capabilities and the ability to add capabilities may be restricted to particular JVMTI phases. The phases ( jvmtiPhase) represent periods of time that the JVMTI environment exists. The key boundaries are: Agent_OnLoad, JVMTI_EVENT_VM_START, JVMTI_EVENT_VM_INIT, and JVMTI_EVENT_VM_DEATH.
The typical vmInit function might look like:
static void JNICALL vmInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) { (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, NULL); } |
Note that in Agent_OnLoad, the callback for the JVMTI_EVENT_DATA_DUMP_REQUEST (Data Dump Request) was already set, we just enabled this event inside the vmInit function.
JVMTI in combination with JNI provides just about everything needed. It is recommended that you browse the JVMTI specification and also look again at all the possible JNI functions that can be used inside a JVMTI agent library. All JVMTI event callbacks get both the jvmtiEnv* pointer and also the JNIEnv* for the current thread, enabling easy JVMTI and JNI access. The interfaces can easily be called from C or C++, but the calls take on a slightly different appearance in the two languages:
jvmtiError err = jvmti->SetTag(object, tag);jvmtiError err = (*jvmti)->SetTag(jvmti, object, tag);#include file "jvmti.h" works for both languages, and both use full prototypes in all cases, so that as long as you avoid needless casts on the addresses of functions, the compilers should find any missing arguments or incorrect type usage. See the demo/jvmti/waiters demo for an example C++ agent library. All of JVMTI will return an error code. It is important that you check these error codes, even though the examples in this document don't.JVMTI_EVENT_VM_INIT will only be called once per VM initialization and you have a little flexibility knowing that it's the only active thread), so that means access to any static or extern data needs to be carefully handled, and any native system functions also need to be MT-safe. Ideally, all your C or C++ functions should be designed in a re-entrant style, and static or extern data should be avoided. The simplest approach to protecting static or global data (as seen in most of the JVMTI demos) is to create a single raw monitor in Agent_OnLoad or vmInit, and use that raw monitor in all the callbacks to assure that only one callback is active at any given point in time (creates a critical section in the native code). This is also the approach that can create performance bottlenecks in the application using the agent library, by preventing threads from executing at the same time. Often times, a per-thread raw monitor, or multiple raw monitors, will be a better performance solution, but may also become a correctness issue with regards to deadlocks if you aren't careful on the nesting orders when parts of the code need both raw monitors.The ability to change the bytecodes of a class has existed in both JVMPI by way of the event JVMPI_EVENT_CLASS_LOAD_HOOK, and in JVMDI by way of RedefineClasses. In fact, by changing the classpath or using class loaders, the same BCI ability has always existed in some basic form. JVMTI just makes all of these possible. It is important to clarify that JVMTI does not actually do the BCI, just allows for it to happen. BCI can be done with various third party or open source classfile transformation libraries or tools. However, in this document I will treat BCI as a subset of what can be done by a class transformation library, so my definition of BCI limits the class transformation to just the addition of bytecodes and constant pool entries, and the necessary changes to any attributes that refer to the bytecode index values or the constant pool index values. Furthermore, it is intended that this BCI operation does not change the basic nature of the classfile's methods. In other words, you can't change the methods to do something functionally different, and you can't add methods or fields to the classfile. The intent with BCI is to insert bytecodes to understand the normal behavior of a class and the methods of that class.
As an example for the Java 2 SDK, the basic hprof ability (that has historically been provided with the Java 2 SDK since version 1.1) was converted from JVMPI to JVMTI. This is the new hprof provided in version 1.5.0, using JVMTI and BCI, and the complete source to this hprof agent, is available in the demo/jvmti directory, along with a fairly detailed HTML user manual. In addition, there is a smaller and simpler demo called heapViewer that should provide a basic understanding of how the JVMTI_EVENT_CLASS_FILE_LOAD_HOOK event works, which is also used by hprof.
Several of the demo/jvmti agents, including hprof and heapViewer, use a small shared demo library called java_crw_demo that is delivered as part of the Java 2 SDK version 1.5.0. The source to this library is also provided in the demo/jvmti directory. The java_crw_demo native library is a primitive classfile transformation library that will insert bytescodes at selected and limited locations in methods, returning a new classfile image. It is important that you understand the classfile layout as described in the Java Virtual Machine Specification. Some of the common issues you may encounter doing classfile transformations are:
max_stack field adjusted along with the code_length . If you add local variables, you will also need to adjust max_locals .java.lang.Object.<init> method. Once the object makes it here, you can pass the object around (or of course you could run Java with the verifier off, but that generally isn't a good idea). The newarray bytecode doesn't have this problem, and in fact the only way to capture these objects is by inserting bytecodes immediately after the newarray bytecode (don't forget the anewarray and multianewarray bytecodes).java_crw_demo, the insertion is limited to pushing a few items on the stack, and making a static method call to a class found in the boot classpath. This so-called tracker class and tracker methods contain Java code that, in our demos, grab the current Thread with a call to java.lang.Thread.currentThread, and pass all the arguments, plus the current thread to a native method belonging to the class. The agent will have registered the natives for this class and those native functions are actually static functions inside the agent library.currentThread is that before the VM is initialized, this thread reference could be null.JVMTI_EVENT_VM_DEATH, making sure the code can recover cleanly.As an example, consider the following Java code:
public class Demo {
private Demo[] newDemo() {
Demo d[] = new Demo[3]; } } |
The hprof agent library does BCI in 4 different places, method entry and method exit for the cpu=times option, and java.lang.Object.<init> and newarray bytecodes for the heap=* option. What hprof (or technically java_crw_demo gets as input) looks basically like this (used javap -c -l -private to dump the class file):
Compiled from "Demo.java" |
After hprof or java_crw_demo gets done with the class, the class would look like:
|
With appropriate changes to other fields in the class file not shown here ( max_stack, constant pool, etc.). The 267309056 number is the class number assigned to this class by the caller of the java_crw_demo library, the second number is the method index for the method getting the insertion. Note the adjustments made to the LineNumberTable and LocalVariableTable.
At runtime, the methods in sun/tools/hprof/Tracker will call native methods that reside inside the agent library, where JVMTI can be used to sample the stack or in general track this BCI event you have created.
JVMTI allows you to attach a tag of type jlong (64 bit value) to any Java object in the Heap. This tag stays with the Java object, including movements caused by compaction after Garbage Collection. JVMTI's object-free event provides you that jlong tag value instead of an object reference, and JVMTI Heap Iterate also provides these tag values as a form of object identification. The tags can be used to uniquely identify an object, or provide a way to categorize objects. Capturing the objects in order to tag them often involves using BCI, but doesn't require it.
A simple non-BCI heap dump can be obtained by tagging all the loaded classes ( jclass objects or java.lang.Class) and then using JVMTI IterateOverHeap:
static jvmtiIterationControl JNICALL
if ( class_tag != (jlong)0 ) {
ClassDetails *d; } }
jclass *classes; char *sig; (*jvmti)->GetClassSignature(jvmti, classes[i], &sig, NULL); } } |
Note that much of the error handling and checking code has been left out of the above example, you should refer to the complete source of this demo for complete details. The above example code does not tell you where these objects were allocated, and not all objects will be accounted for (primitive classes are not included), but it does provide a quick and easy way to find statistics on the space used by the various objects in the heap. Some common tips around tags and heap iterations:
ptrdiff_t(Standard C typedef for an integer that holds a pointer difference) to avoid compiler warnings and errors. Never use int or long, you'll find out that int and long are not always big enough to hold a pointer, truncating your address, where ptrdiff_t will always be big enough to hold all the bits of a pointer.JVMTI_EVENT_VM_OBJECT_ALLOC event.GetCurrentThreadCpuTime over GetThreadCpuTime. Access to the current thread data will almost always be faster than accessing thread data from another thread.RawMonitorEnter calls inside BCI code. Holding locks in BCI code can destroy performance on multiple-CPU machines.GetThreadListStackTraces instead of looping over GetStackTrace.jobject, jstring, jclass, or jthread) that need to be saved. These will need JNI Global or Weak references created for them, and you will need to manage these references, using JNI to delete them when they are no longer needed.jvmtiError returns, you'd be surprised how important it is to find these non- JVMTI_ERROR_NONE error returns early in the agent library execution.JVMTI_ERROR_WRONG_PHASE errors are your friends, they are likely telling you that the agent library has a thread race condition. The older interfaces JVMPI and JVMDI did not have this feature, and with JVMTI having documented and specified phases where everything can be called, these PHASE errors can help you track down synchronization errors during agent startup and shutdown.Deallocate.Allocate to allocate all your agent memory needs, or you can use the system malloc functions. If you choose to use malloc or something other than JVMTI, be very careful not to mix these up. Using multiple memory allocation schemes in your native code can be error-prone.Agent_OnLoad and Agent_OnUnload. On Windows only, export the Agent_OnLoad and Agent_OnUnload externs. In general, this is just a safe way to build your shared library.demo/jvmti/index.html page for help in selecting the right C and C++ compiler options.#define LOG(a) ( gdata->logflags != 0 ? printf a : 0 ) LOG(("Entered callback %s\n", VMStart)); |
pause=y option that will pause the process at Agent_OnLoad option parsing time, allowing for a debugger to attach to the process. Have the pause=y option print out a message containing the PID (see getpid).' java -XX:TraceJVMTI= desc {, desc } ... ' where each desc is composed of 3 fields (no blanks) ' domain action kind '. The domain field is one of name (a particular function or event name), all (all functions and events), func (all major functions), allfunc (all functions), event (all events), or ec (event controller). The action field is '+' to add or '-'to remove. The kind field is 'i' (input params), 'e' (error returns), 'o' (output), 't' (event triggered a.k.a. posted), or 's' (event sent). An example: java-XX:TraceJVMTI=ec+,GetCallerFrame+ie,Breakpoint+s ... -Xcheck:jni How easy the transition from JVMPI to JVMTI is really depends on the complexity of the specific agent library, how much of JVMPI was used, and using JVMTI in such a way that performance of the target application doesn't suffer. Some of the conversion will be fairly straightforward, but other parts may take some time and even require several iterations over the design. Don't underestimate this effort.
JVMTI is part of Java Specification Request 163 ( JSR-163) of the Java Community Process ( JCP).
All the examples in this document have been taken from operating JVMTI demo agents available with the Java 2 SDK version 1.5.0. To see the latest JVMTI demos, just point your browser to the demo/jvmti directory of your Java 2 SDK installation and you should have access to all the demo documents plus the complete source of all the JVMTI demos in the file demo/jvmti/src.zip. Downloads are available at the J2SE 1.5.0 Beta 2 downloads page.
1 As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.