Analyse Heap at every garbage collection

Hello

I am trying to create a program that will print out the classes that are loaded after every garbage collection

As a solution, I thought I would try and merge together the gctest and heapviewer examples that are in the demo/JVMTI folder.

I have tried many different variations and still can抰 get it to work properly. Depending on what I do, either my program skips heap dumps (i.e. when a garbage collection occurs, it sometimes doesn抰 bother printing the heap) or it just causes deadlock.

Am I taking the wrong approach to solving this problem?

Many Thanks

Sajid

[611 byte] By [SajidTendulkara] at [2007-10-2 22:42:41]
# 1

You've got the basis for a solution with gctest and heapViewer. I suspect you'll need to post more details on the issues if you are to get help. For example, what do you mean it by it "skips heap dumps" - are you saying that the GarbageCollectionFinish event isn't delivered or are you saying that the IterateThroughHeap is failing?

alan.batemana at 2007-7-14 5:57:06 > top of Java-index,Developer Tools,Debugging and Profiling Tool APIs...
# 2

Hello

Thank you for your reply. I'll show the code that is giving me the "best" result. When I execute the code, all the events get executed, but you will see that even when it is in the middle of a dump, it still keeps sending the events. To fix this, i tried moving the call to dataDumpRequest() in line 213 a few lines up (i.e. when the raw monitor has been entered), but this causes the program to freeze. Then, in the actual dataDumpRequest() method, i put lines at the start and end to enter and exit the raw monitor, but this was causing the program to freeze too.

the include statements seem to have gone missing below. I am using stdio.h, stddef.h, stdlib.h and string.h

<pre>

#include <stdio.h>

#include <stddef.h>

#include <stdlib.h>

#include <string.h>

#include "jvmti.h"

#include "jni.h"

static jvmtiEnv *jvmti;

static jrawMonitorID lock;

static int gc_count;

static int totalCount;

/* Typedef to hold class details */

typedef struct {

char *signature;

intcount;

intspace;

} ClassDetails;

/* Check for NULL pointer error */

#define CHECK_FOR_NULL(ptr) \

checkForNull(ptr, __FILE__, __LINE__)

static void checkForNull(void *ptr, char *file, int line)

{

if ( ptr == NULL ) {

fprintf(stderr, "ERROR: NULL pointer error in %s:%d\n", file, line);

abort();

}

}

/* Deallocate JVMTI memory */

static void deallocate(jvmtiEnv *jvmti, void *p)

{

jvmtiError err;

err = (*jvmti)->Deallocate(jvmti, (unsigned char *)p);

if ( err != JVMTI_ERROR_NONE ) {

fprintf(stderr, "ERROR: JVMTI Deallocate error err=%d\n", err);

abort();

}

}

/* Get name for JVMTI error code */

static char * getErrorName(jvmtiEnv *jvmti, jvmtiError errnum)

{

jvmtiError err;

char*name;

err = (*jvmti)->GetErrorName(jvmti, errnum, &name);

if ( err != JVMTI_ERROR_NONE ) {

fprintf(stderr, "ERROR: JVMTI GetErrorName error err=%d\n", err);

abort();

}

return name;

}

/* Check for JVMTI error */

#define CHECK_JVMTI_ERROR(jvmti, err) \

checkJvmtiError(jvmti, err, __FILE__, __LINE__)

static void checkJvmtiError(jvmtiEnv *jvmti, jvmtiError err, char *file, int line)

{

if ( err != JVMTI_ERROR_NONE ) {

char *name;

name = getErrorName(jvmti, err);

fprintf(stderr, "ERROR: JVMTI error err=%d(%s) in %s:%d\n",

err, name, file, line);

deallocate(jvmti, name);

abort();

}

}

/* Heap object callback */

static jvmtiIterationControl JNICALL heapObject(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)

{

if ( class_tag != (jlong)0 ) {

ClassDetails *d;

d = (ClassDetails*)(void*)(ptrdiff_t)class_tag;

totalCount++;

d->count++;

d->space += size;

}

return JVMTI_ITERATION_CONTINUE;

}

/* Compare two ClassDetails */

static int compareDetails(const void *p1, const void *p2)

{

return ((ClassDetails*)p2)->space - ((ClassDetails*)p1)->space;

}

static void dataDumpRequest()

{

// (*jvmti)->RawMonitorEnter(jvmti, lock);

{

jvmtiErrorerr;

void *user_data;

jclass*classes;

jint count;

jint i;

ClassDetails *details;

totalCount = 0;

/* Get all the loaded classes */

err = (*jvmti)->GetLoadedClasses(jvmti, &count, &classes);

CHECK_JVMTI_ERROR(jvmti, err);

/* Setup an area to hold details about these classes */

details = (ClassDetails*)calloc(sizeof(ClassDetails), count);

CHECK_FOR_NULL(details);

for ( i = 0 ; i < count ; i++ )

{

char *sig;

/* Get and save the class signature */

err = (*jvmti)->GetClassSignature(jvmti, classes, &sig, NULL);

CHECK_JVMTI_ERROR(jvmti, err);

CHECK_FOR_NULL(sig);

details.signature = strdup(sig);

deallocate(jvmti, sig);

/* Tag this jclass */

err = (*jvmti)->SetTag(jvmti, classes, (jlong)(ptrdiff_t)(void*)(&details));

CHECK_JVMTI_ERROR(jvmti, err);

}

/* Iterate over the heap and count up uses of jclass */

err = (*jvmti)->IterateOverHeap(jvmti, JVMTI_HEAP_OBJECT_EITHER, &heapObject, NULL);

CHECK_JVMTI_ERROR(jvmti, err);

/* Remove tags */

for ( i = 0 ; i < count ; i++ )

{

/* Un-Tag this jclass */

err = (*jvmti)->SetTag(jvmti, classes, (jlong)0);

CHECK_JVMTI_ERROR(jvmti, err);

}

/* Sort details by space used */

qsort(details, count, sizeof(ClassDetails), &compareDetails);

/* Print out sorted table */

fprintf(stdout, "Heap View, Total of %d objects found.\n\n", totalCount);

fprintf(stdout, "SpaceCountClass Signature\n");

fprintf(stdout, "- - -\n");

for ( i = 0 ; i < count ; i++ )

{

if ( details.space == 0 || i > 20 )

{

break;

}

fprintf(stdout, "%10d %10d %s\n",

details.space, details.count, details.signature);

}

fprintf(stdout, "- - -\n\n");

fflush(stdout);

/* Free up all allocated space */

deallocate(jvmti, classes);

for ( i = 0 ; i < count ; i++ )

{

if ( details.signature != NULL )

{

free(details.signature);

}

}

free(details);

}

// (*jvmti)->RawMonitorExit(jvmti, lock);

}

/* Worker thread that waits for garbage collections */

static void JNICALL worker2(jvmtiEnv* jvmti, JNIEnv* jni, void *p)

{

jvmtiErrorerr;

fprintf(stderr, "GC worker started...\n");

for (;;)

{

err = (*jvmti)->RawMonitorEnter(jvmti, lock);

if (err != JVMTI_ERROR_NONE)

{

fprintf(stderr, "ERROR: RawMonitorEnter failed, err=%d\n", err);

return;

}

while (gc_count == 0)

{

err = (*jvmti)->RawMonitorWait(jvmti, lock, 0);

if (err != JVMTI_ERROR_NONE)

{

fprintf(stderr, "ERROR: RawMonitorWait failed, err=%d\n", err);

err = (*jvmti)->RawMonitorExit(jvmti, lock);

CHECK_JVMTI_ERROR(jvmti, err);

return;

}

}

gc_count = 0;

err = (*jvmti)->RawMonitorExit(jvmti, lock);

CHECK_JVMTI_ERROR(jvmti, err);

dataDumpRequest();

/* Perform arbitrary JVMTI/JNI work here to do post-GC cleanup */

fprintf(stderr, "post-GarbageCollectionFinish actions...\n");

}

}

/* Creates a new jthread */

static jthread alloc_thread(JNIEnv *env)

{

jclassthrClass;

jmethodID cid;

jthreadres;

thrClass = (*env)->FindClass(env, "java/lang/Thread");

cid= (*env)->GetMethodID(env, thrClass, "<init>", "()V");

res= (*env)->NewObject(env, thrClass, cid);

return res;

}

static void JNICALL vmInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)

{

jvmtiErrorerr;

err = (*jvmti)->RunAgentThread(jvmti, alloc_thread(env), &worker2, NULL,JVMTI_THREAD_MAX_PRIORITY);

CHECK_JVMTI_ERROR(jvmti, err);

fprintf(stderr, "VMInit...\n");

}

static void JNICALL gc_start(jvmtiEnv *jvmti_env)

{

jvmtiError err;

fprintf(stderr, "GarbageCollectionStart...\n");

}

static void JNICALL gc_finish(jvmtiEnv *jvmti_env)

{

jvmtiErrorerr;

fprintf(stderr, "GarbageCollectionFinish...\n");

err = (*jvmti)->RawMonitorEnter(jvmti, lock);

CHECK_JVMTI_ERROR(jvmti, err);

gc_count++;

err = (*jvmti)->RawMonitorNotify(jvmti, lock);

CHECK_JVMTI_ERROR(jvmti, err);

err = (*jvmti)->RawMonitorExit(jvmti, lock);

CHECK_JVMTI_ERROR(jvmti, err);

}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)

{

jint rc;

jvmtiCapabilities capa;

jvmtiEventCallbacks callbacks;

//get jvmti environment

rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);

(*jvmti)->GetCapabilities(jvmti, &capa);

capa.can_tag_objects = 1;

capa.can_generate_garbage_collection_events = 1;

(*jvmti)->AddCapabilities(jvmti, &capa);

(*jvmti)->CreateRawMonitor(jvmti, "lock", &lock);

memset(&callbacks, 0, sizeof(callbacks));

callbacks.VMInit= &vmInit;

callbacks.GarbageCollectionStart = &gc_start;

callbacks.GarbageCollectionFinish = &gc_finish;

(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));

(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);

(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL);

(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL);

return JNI_OK;

}

</pre>

SajidTendulkara at 2007-7-14 5:57:06 > top of Java-index,Developer Tools,Debugging and Profiling Tool APIs...
# 3

You don't want to hold the worker/gc lock during the data dump request. I suspect you might be getting a GC event during the data dump request and if you are holding this lock, you will freeze. The lock that is in this code should be reserved for just indicating that a gc event has happened and holding it should be done just to update gc_count and do the notify/wait. You can't use this lock for anything else.

I think you need a different lock in the data dump request that protects it from being executed multiple times, what you have will work but will probably give you skewed results if two of these things are happening at once... Humm now that I think about the tags, it will be bad, you don't want two data dump requests to happen at the same time unless you changed the data dump request to use a new jvmti environment for each request, which seems like overkill. You can either toss the second request that happens when you are doing a dump, or do something to queue them up. Since you are really getting an 'after the fact' notification that a gc happened, you probably want to just toss it.

Hope this helps. If you get it working you'll have a JVM agent writing scar you can show your friends and family. ;^)

-kto

kellyohaira at 2007-7-14 5:57:06 > top of Java-index,Developer Tools,Debugging and Profiling Tool APIs...