JVMTI (JVM Tool Interface) is a native programming interface for inspecting the state and controlling the execution of applications running in the Java Virtual Machine. It provides capabilities for profiling, debugging, monitoring, thread analysis, and coverage analysis.
Overview
JVMTI is defined in src/hotspot/share/prims/jvmti.xml and implemented across multiple files in the prims directory. It replaces the older JVMPI (JVM Profiler Interface) and JVMDI (JVM Debug Interface).
Key Features
- Event-based monitoring: Receive notifications for VM events (class loading, method entry/exit, etc.)
- Heap inspection: Walk the heap, tag objects, monitor allocations
- Thread control: Suspend/resume threads, inspect stack frames
- Bytecode instrumentation: Redefine and retransform classes
- Breakpoint support: Set breakpoints and field watchpoints
- Local variable access: Read and modify local variables
JVMTI Environment
Each JVMTI agent receives a jvmtiEnv pointer that represents its interface to the JVM:
typedef struct jvmtiEnv_struct {
const struct jvmtiInterface_1_ *functions;
} jvmtiEnv;
From src/hotspot/share/prims/jvmtiEnv.cpp:91-100:
JvmtiEnv::JvmtiEnv(jint version) : JvmtiEnvBase(version) {
}
JvmtiEnv::~JvmtiEnv() {
}
JvmtiEnv*
JvmtiEnv::create_a_jvmti(jint version) {
return new JvmtiEnv(version);
}
Capabilities
JVMTI uses capabilities to control which features are available to an agent. Agents must explicitly request capabilities before using certain functions.
Capabilities Structure
From src/hotspot/share/prims/jvmti.xml, the jvmtiCapabilities structure contains boolean flags:
Heap Capabilities
Class Capabilities
Debug Capabilities
Thread Capabilities
Can set and get tags for heap objects, required for heap iteration and object tagging
can_generate_garbage_collection_events
Can generate events when garbage collection begins and ends
can_generate_object_free_events
Can generate events when the garbage collector frees an object
Can get bytecodes of a method via GetBytecodes()
can_get_synthetic_attribute
Can test if a field or method is synthetic
Can redefine classes with RedefineClasses()
Can retransform classes with RetransformClasses()
Can get source file name via GetSourceFileName()
Can get line number table via GetLineNumberTable()
can_generate_field_modification_events
Can set watchpoints on field modification via SetFieldModificationWatch()
can_generate_field_access_events
Can set watchpoints on field access via SetFieldAccessWatch()
can_generate_breakpoint_events
Can set breakpoints via SetBreakpoint()
can_generate_frame_pop_events
Can generate events when frames are popped from the stack
can_generate_method_entry_events
Can generate events on method entry
can_generate_method_exit_events
Can generate events on method exit
Can suspend and resume threads
Can signal threads with interrupt and stop
can_get_owned_monitor_info
Can get information about monitors owned by a thread
can_get_current_contended_monitor
Can get the monitor a thread is waiting to enter
Can get information about a monitor
Managing Capabilities
jvmtiError
AddCapabilities(jvmtiEnv* env,
const jvmtiCapabilities* capabilities_ptr)
jvmtiError
RelinquishCapabilities(jvmtiEnv* env,
const jvmtiCapabilities* capabilities_ptr)
jvmtiError
GetCapabilities(jvmtiEnv* env,
jvmtiCapabilities* capabilities_ptr)
Capabilities must be added during the OnLoad phase or during the live phase. Some capabilities can only be added during OnLoad.
Agent Initialization
JVMTI agents are loaded as native libraries and initialized via specific entry points:
Agent_OnLoad
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jvmtiEnv *jvmti;
jvmtiError error;
jvmtiCapabilities capabilities;
// Get JVMTI environment
(*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_2);
// Request capabilities
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_tag_objects = 1;
capabilities.can_generate_garbage_collection_events = 1;
error = (*jvmti)->AddCapabilities(jvmti, &capabilities);
// Set event callbacks
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMInit = &vmInitCallback;
callbacks.GarbageCollectionStart = &gcStartCallback;
error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
// Enable events
error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_VM_INIT, NULL);
return JNI_OK;
}
Agent Phases
Agent library loaded, JVMTI functions are limited. VM not initialized.
After OnLoad, before VMStart. Very few JVMTI functions are available.
VM is started but not fully initialized. More functions become available.
VM is fully running. All JVMTI functions are available.
VM has terminated. Limited JVMTI functions available.
Events
JVMTI provides extensive event notifications for monitoring VM execution:
Event Callbacks Structure
typedef struct {
jvmtiEventVMInit VMInit;
jvmtiEventVMDeath VMDeath;
jvmtiEventThreadStart ThreadStart;
jvmtiEventThreadEnd ThreadEnd;
jvmtiEventClassFileLoadHook ClassFileLoadHook;
jvmtiEventClassLoad ClassLoad;
jvmtiEventClassPrepare ClassPrepare;
jvmtiEventVMStart VMStart;
jvmtiEventException Exception;
jvmtiEventExceptionCatch ExceptionCatch;
jvmtiEventMethodEntry MethodEntry;
jvmtiEventMethodExit MethodExit;
jvmtiEventNativeMethodBind NativeMethodBind;
jvmtiEventCompiledMethodLoad CompiledMethodLoad;
jvmtiEventCompiledMethodUnload CompiledMethodUnload;
jvmtiEventDynamicCodeGenerated DynamicCodeGenerated;
jvmtiEventGarbageCollectionStart GarbageCollectionStart;
jvmtiEventGarbageCollectionFinish GarbageCollectionFinish;
jvmtiEventObjectFree ObjectFree;
// ... more events
} jvmtiEventCallbacks;
Common Event Callbacks
VM Events
Thread Events
Class Events
Method Events
GC Events
void JNICALL vmInitCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread)
{
// VM initialization complete
}
void JNICALL vmDeathCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env)
{
// VM about to exit
}
void JNICALL threadStartCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread)
{
// New thread started
}
void JNICALL threadEndCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread)
{
// Thread about to terminate
}
void JNICALL classLoadCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jclass klass)
{
// Class has been loaded
}
void JNICALL classPrepareCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jclass klass)
{
// Class has been prepared
}
void JNICALL methodEntryCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method)
{
// Method entry (requires can_generate_method_entry_events)
}
void JNICALL methodExitCallback(
jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jboolean was_popped_by_exception,
jvalue return_value)
{
// Method exit (requires can_generate_method_exit_events)
}
void JNICALL gcStartCallback(
jvmtiEnv *jvmti_env)
{
// Garbage collection started
}
void JNICALL gcFinishCallback(
jvmtiEnv *jvmti_env)
{
// Garbage collection finished
}
void JNICALL objectFreeCallback(
jvmtiEnv *jvmti_env,
jlong tag)
{
// Tagged object freed by GC
}
Heap Inspection
JVMTI provides powerful capabilities for heap analysis:
Object Tagging
jvmtiError
SetTag(jvmtiEnv* env,
jobject object,
jlong tag)
jvmtiError
GetTag(jvmtiEnv* env,
jobject object,
jlong* tag_ptr)
jvmtiError
GetObjectsWithTags(jvmtiEnv* env,
jint tag_count,
const jlong* tags,
jint* count_ptr,
jobject** object_result_ptr,
jlong** tag_result_ptr)
Heap Iteration
jvmtiError
IterateThroughHeap(jvmtiEnv* env,
jint heap_filter,
jclass klass,
const jvmtiHeapCallbacks* callbacks,
const void* user_data)
jvmtiError
FollowReferences(jvmtiEnv* env,
jint heap_filter,
jclass klass,
jobject initial_object,
const jvmtiHeapCallbacks* callbacks,
const void* user_data)
Heap Callback Example
jint JNICALL heapObjectCallback(
jlong class_tag,
jlong size,
jlong* tag_ptr,
void* user_data)
{
// Called for each object in heap
if (*tag_ptr == 0) {
*tag_ptr = ++object_counter;
}
return JVMTI_VISIT_OBJECTS; // Continue iteration
}
Stack Frame Access
Access and manipulate thread stack frames:
jvmtiError
GetStackTrace(jvmtiEnv* env,
jthread thread,
jint start_depth,
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr)
jvmtiError
GetLocalVariable(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jvalue* value_ptr)
jvmtiError
SetLocalVariable(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jvalue value)
Class Redefinition
Dynamically redefine classes at runtime:
typedef struct {
jclass klass;
jint class_byte_count;
const unsigned char* class_bytes;
} jvmtiClassDefinition;
jvmtiError
RedefineClasses(jvmtiEnv* env,
jint class_count,
const jvmtiClassDefinition* class_definitions)
jvmtiError
RetransformClasses(jvmtiEnv* env,
jint class_count,
const jclass* classes)
Class redefinition has limitations:
- Cannot add or remove methods or fields
- Cannot change class hierarchy
- Cannot change method signatures
- Requires can_redefine_classes or can_retransform_classes capability
Thread Control
jvmtiError
SuspendThread(jvmtiEnv* env,
jthread thread)
jvmtiError
ResumeThread(jvmtiEnv* env,
jthread thread)
jvmtiError
GetThreadInfo(jvmtiEnv* env,
jthread thread,
jvmtiThreadInfo* info_ptr)
jvmtiError
GetAllThreads(jvmtiEnv* env,
jint* threads_count_ptr,
jthread** threads_ptr)
Raw Monitors
JVMTI raw monitors provide synchronization independent of Java monitors:
jvmtiError
CreateRawMonitor(jvmtiEnv* env,
const char* name,
jrawMonitorID* monitor_ptr)
jvmtiError
RawMonitorEnter(jvmtiEnv* env,
jrawMonitorID monitor)
jvmtiError
RawMonitorExit(jvmtiEnv* env,
jrawMonitorID monitor)
jvmtiError
RawMonitorWait(jvmtiEnv* env,
jrawMonitorID monitor,
jlong millis)
jvmtiError
RawMonitorNotify(jvmtiEnv* env,
jrawMonitorID monitor)
Error Codes
typedef enum {
JVMTI_ERROR_NONE = 0,
JVMTI_ERROR_INVALID_THREAD = 10,
JVMTI_ERROR_INVALID_THREAD_GROUP = 11,
JVMTI_ERROR_INVALID_PRIORITY = 12,
JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13,
JVMTI_ERROR_THREAD_SUSPENDED = 14,
JVMTI_ERROR_THREAD_NOT_ALIVE = 15,
JVMTI_ERROR_INVALID_OBJECT = 20,
JVMTI_ERROR_INVALID_CLASS = 21,
JVMTI_ERROR_CLASS_NOT_PREPARED = 22,
JVMTI_ERROR_INVALID_METHODID = 23,
JVMTI_ERROR_INVALID_FIELDID = 25,
JVMTI_ERROR_OUT_OF_MEMORY = 110,
JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99,
// ... more error codes
} jvmtiError;
Always check return values from JVMTI functions. Most errors indicate incorrect API usage or missing capabilities.