Использование JVMTI для получения объема памяти, освобожденного GC - PullRequest
2 голосов
/ 31 мая 2011

Я пытаюсь использовать JVMTI, чтобы узнать, сколько памяти было освобождено GC, это будет использоваться как часть профилировщика.

Используя JVMTI, я могу получать события для GC_START и GC_END.JVMTI также предоставляет возможности для обхода кучи, и из этого я могу получить ее точный текущий размер.Логически я мог бы получить размер кучи в GC_START и GC_END, а затем получить разницу в размере кучи.

Проблема в том, что хотя функции обработчика событий GC_START и GC_END отключены, большинство функций JVMTI отключено, и я получаю JVMTI_ERROR_UNATTACHED_THREAD (115) ошибка.

Если я посмотрю на ссылку API JVMTI http://download.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#GarbageCollectionStart

«Запуск сборки мусора» Это событие отправляется, когда виртуальная машина все еще остановлена, поэтому обработчик событий не должен использовать JNIфункций и не должны использовать функции TI JVM, кроме тех, которые специально разрешают такое использование (см. функции необработанного монитора, управления памятью и локального хранилища среды). ”

Так что, похоже, я не могу получить доступ к памяти из обработчика событий.

Ошибка генерируется в функции GetCurrentHeapMemory.

Код выглядит следующим образом / * * memory_collector.c * * Создано: 8 мая 2011 * Автор: 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 Ответ

1 голос
/ 16 апреля 2012

Поскольку ваш метод обратного вызова getCurrentHeapMemory() вызывается для собственного метода, который не получает JNIEnv, поток не имеет доступа к JVM и, следовательно, к любым объектам внутри JVM, то есть кучи объектов в этом случае.

Чтобы получить доступ, выполните одно из следующих действий:

  1. Присоедините текущий поток к JVM перед вызовом IterateOverHeap, выполнив AttachCurrentThread который предоставит доступ к объектам в JVM.

  2. В качестве альтернативы интерфейс JVMTI предоставляет удобный способ сделать это для вас (и заботится об отделении), который вы можете использовать с помощью RunAgentThread API при запуске вашего метода, который обходит кучу.

...