Skip to main content
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:
can_tag_objects
jboolean
Can set and get tags for heap objects, required for heap iteration and object tagging
can_generate_garbage_collection_events
jboolean
Can generate events when garbage collection begins and ends
can_generate_object_free_events
jboolean
Can generate events when the garbage collector frees an object

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

OnLoad
phase
Agent library loaded, JVMTI functions are limited. VM not initialized.
Primordial
phase
After OnLoad, before VMStart. Very few JVMTI functions are available.
Start
phase
VM is started but not fully initialized. More functions become available.
Live
phase
VM is fully running. All JVMTI functions are available.
Dead
phase
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

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
}

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.

Build docs developers (and LLMs) love