JVMTI を使用して、GC によって解放されたメモリの量を把握しようとしています。これは、プロファイラーの一部として使用されます。
JVMTI を使用して、GC_START および GC_END のイベントを取得できます。JVMTI は、ヒープをウォークスルーする機能も提供しており、そこから正確な現在のサイズを取得できます。論理的には、GC_START と GC_END でヒープ サイズを取得してから、ヒープ サイズの差を取得できます。
問題は、GC_START および GC_END イベント ハンドラーが機能している間、ほとんどの JVMTI 機能が無効になり、JVMTI_ERROR_UNATTACHED_THREAD (115) エラーが発生することです。
JVMTI APIリファレンス http://download.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#GarbageCollectionStartを見ると
「ガベージ コレクションの開始 このイベントは、VM がまだ停止している間に送信されます。したがって、イベント ハンドラは、JNI 関数を使用してはならず、JVM TI 関数を使用してはなりません。ただし、そのような使用を明確に許可するものを除きます (raw モニタ、メモリ管理、および環境ローカルを参照してください)。ストレージ機能)」</p>
そのため、イベント ハンドラからメモリにアクセスできないようです。
エラーは、GetCurrentHeapMemory 関数でスローされます。
コードは次のとおりです /* * memory_collector.c * * 作成日: 2011 年 5 月 8 日 * 作成者: 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 capabilities;
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;
}
JVMTI を使用してこの情報を収集する方法に関する情報を入手できれば幸いです。JVMTI の代替手段も歓迎します。
ありがとう