Using JVMTI to get the amount of memory freed by the GC
I'm trying to use JVMTI to know how much memory was freed by the GC, this would be used as part of a profiler.
Using JVMTI I can get events for the GC_START and GC_END. JVMTI also provides facilities to walk over the heap and from that I can get its exact current size. Logically I could get the heap size on GC_START and GC_END and then get the heap size difference.
The problem is that while the GC_START and GC_END event handler functions most of the JVMTI functionality is disabled and I get an JVMTI_ERROR_UNATTACHED_THREAD (115) error.
If I look at the JVMTI API reference http://download.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#GarbageCollectionStart
“Garbage Collection Start This event is sent while the VM is still stopped, thus the event handler must not use JNI functions and must not use JVM TI functions except those which specifically allow such use (see the raw monitor, memory management, and environment local storage functions).”
So it seems I cannot access the memory from the event handler.
The error is thrown in the GetCurrentHeapMemory function.
The code is as follows /* * memory_collector.c * * Created on: May 8, 2011 * Author: ycarel */
#include "memory_collector.h"
#include <stdlib.h>
#include <memory.h>
#include "globals.h"
/* Heap object callback */
static jvmtiIterationControl JNICALL accumulateHeap(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)
//(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data)
{
jint *total;
total = (jint *)user_data;
(*total)+=size;
return JVMTI_ITERATION_CONTINUE;
}
jlong getCurrentHeapMemory()
{
jint totalCount=0;
jint rc;
/* This returns the JVMTI_ERROR_UNATTACHED_THREAD */
rc = gdata.jvmti->IterateOverHeap((jvmtiHeapObjectFilter)0 ,&accumulateHeap,&totalCount);
//(0, &heapCallbacks, &totalCount);
if (rc != JVMTI_ERROR_NONE)
{
printf("Iterating over heap objects failed, returning error %d\n",rc);
return MEMORY_COLL_ERROR;
} else {
printf("Heap memory calculated %d\n",totalCount);
}
return totalCount;
}
/* Callback for JVMTI_EVENT_GARBAGE_COLLECTION_START */
static void JNICALL gc_start(jvmtiEnv* jvmti_env)
{
jint rc;
printf("Garbage Collection Started...\n");
rc = gdata.jvmti->RawMonitorEnter(gdata.lock);
if (rc != JVMTI_ERROR_NONE)
{
printf("Failed to get lock for heap memory collection, skipping gc_start collection\n");
return;
}
getCurrentHeapMemory();
rc = gdata.jvmti->RawMonitorExit(gdata.lock);
if (rc != JVMTI_ERROR_NONE)
{
printf("Failed to release lock for heap memory collection, skipping gc_start collection\n");
return;
}
}
/* Callback for JVMTI_EVENT_GARBAGE_COLLECTION_END */
static void JNICALL gc_end(jvmtiEnv* jvmti_env)
{
printf("Garbage Collection Ended...\n");
}
static void JNICALL vm_init(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
{
printf("vm_init called\n");
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiCapabilities ca开发者_StackOverflowpabilities;
jvmtiEventCallbacks callbacks;
/* Here goes the code for initalisation removed for making the code readble */
memset(&callbacks, 0x00, sizeof(callbacks));
callbacks.GarbageCollectionStart = gc_start;
callbacks.GarbageCollectionFinish = gc_end;
callbacks.VMInit = vm_init;
rc = gdata.jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
if (rc != JVMTI_ERROR_NONE)
{
printf("Failed to set JVMTI event handlers, quitting\n");
return JNI_ERR;
}
rc = gdata.jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_GARBAGE_COLLECTION_START,NULL);
rc &= gdata.jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,NULL);
rc &= gdata.jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_VM_INIT,NULL);
if (rc != JVMTI_ERROR_NONE)
{
printf("Failed to set JVMTI event notification mode, quitting\n");
return JNI_ERR;
}
return JNI_OK;
}
I would be glad to get information on how to collect this information using JVMTI, alternatives to JVMTI would be also appreciated.
Thanks
Since your callback method getCurrentHeapMemory()
is invoked on a native method that does not get the JNIEnv
the thread does not have access to the JVM, and therefore to any objects inside the JVM i.e the object heap in this instance.
You do either of this to gain access:
Attach the current thread to the JVM before making the call to IterateOverHeap by doing an AttachCurrentThread which will give access to the objects in the JVM.
Alternatively the JVMTI interface provides a convenience method to do this for you (and takes care of detach) which you can leverage with the RunAgentThread API when launching your method that does walks the heap.
精彩评论