使用JVMTI获取GC释放的内存量

时间:2011-05-31 08:21:28

标签: java profiling jvmti

我正在尝试使用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的替代方案也将受到赞赏。

由于

1 个答案:

答案 0 :(得分:1)

由于您的回调方法getCurrentHeapMemory()是在未获取JNIEnv的本机方法上调用的,因此该线程无法访问JVM,因此无法访问JVM内的任何对象,即对象堆在这种情况下。

您可以执行以下任一操作以获取访问权限:

  1. 在通过执行AttachCurrentThread调用IterateOverHeap之前将当前线程附加到JVM,这将允许访问JVM中的对象。

  2. 或者,JVMTI接口提供了一种方便的方法来为您执行此操作(并负责分离),您可以在启动执行堆的方法时使用RunAgentThread API。