【发布时间】:2011-05-31 08:21:28
【问题描述】:
我正在尝试使用 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 函数,除非那些特别允许使用的函数(请参阅原始监视器、内存管理和环境本地存储函数)。 ”
看来我无法从事件处理程序访问内存。
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 的替代品。
谢谢
【问题讨论】:
-
就我个人而言,我会将 GC 日志记录到一个文件中并对其进行解析。 AFAIK,您只能获得使用量的快照,当使用的内存减少时,您需要假设内存已被 GC 释放,但实际数量可能会略高。
-
@peter 我不确定我是否理解你对“GC 日志”的意思,你能提供一个参考吗?谢谢
-
你可以用
-Xloggc:filestackoverflow.com/questions/1161647/…重定向详细的gc日志你可以根据需要添加其他gc信息。