Я развернул простой агент JVMTI для тестирования инструментария байт-кода. Моей стратегией является вызов функции RetransformClasses
в CompiledMethodLoad
для вызова ClassFileLoadHook
. Я написал следующий код, чтобы сделать это:JVMTI RetransformClasses() занимает много времени
err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
check_jvmti_error(jvmti, err, "Get Declaring Class");
err = (*jvmti)->RetransformClasses(jvmti, 1, &klass);
check_jvmti_error(jvmti, err, "Retransform class");
Эта функция работает правильно, вызывая ClassFileLoadHook
событие, но это занимает много времени, пока я просто мимо того же класса внутри него. Моя функция обратного вызова ClassFileLoadHook
пуста. Я рассчитываю время простого алгоритма умножения матрицы. Комментируя функцию RetransformClasses
, я получаю время выполнения порядка 0.8 seconds
. В то время как просто запись этой функции увеличивает время выполнения примерно до 15 seconds
.
Предполагается, что это займет слишком много накладных расходов или я делаю что-то неправильно?
С уважением
Код:
static int x = 1;
void JNICALL
compiled_method_load(jvmtiEnv *jvmti, jmethodID method, jint code_size,
const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map,
const void* compile_info) {
jvmtiError err;
jclass klass;
char* name = NULL;
char* signature = NULL;
char* generic_ptr = NULL;
err = (*jvmti)->RawMonitorEnter(jvmti, lock);
check_jvmti_error(jvmti, err, "raw monitor enter");
err = (*jvmti)->GetMethodName(jvmti, method, &name, &signature,
&generic_ptr);
check_jvmti_error(jvmti, err, "Get Method Name");
printf("\nCompiled method load event\n");
printf("Method name %s %s %s\n\n", name, signature,
generic_ptr == NULL ? "" : generic_ptr);
if (strstr(name, "main") != NULL && x == 1) {
x++;
err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
check_jvmti_error(jvmti, err, "Get Declaring Class");
err = (*jvmti)->RetransformClasses(jvmti, 1, &klass);
check_jvmti_error(jvmti, err, "Retransform class");
}
if (name != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) name);
check_jvmti_error(jvmti, err, "deallocate name");
}
if (signature != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) signature);
check_jvmti_error(jvmti, err, "deallocate signature");
}
if (generic_ptr != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) generic_ptr);
check_jvmti_error(jvmti, err, "deallocate generic_ptr");
}
err = (*jvmti)->RawMonitorExit(jvmti, lock);
check_jvmti_error(jvmti, err, "raw monitor exit");
}
Ну, ретрансформация не из дешевых. Выполняете ли вы код выше для всех скомпилированных методов?'CompiledMethodLoad' вызывается для горячих методов, которые сильно влияют на время выполнения, и, ретранслируя их классы, вы 1) платите стоимость воссоздания класса 2) бросаете скомпилированные методы, эффективно заставляя VM выполнять горячие методы в интерпретаторе –
Я просто переформатирование одного файла класса и он содержит только один метод. Это не должно быть дорого. Нет никакого сравнения между «0,8 секунды» и «15» секунд. Это не похоже на стоимость. Более того, даже если программа запускается в интерпретаторе, из-за режима «TieredCompilation», он должен снова вернуться в скомпилированный режим. Поправьте меня если я ошибаюсь. \\ На самом деле я хочу использовать только «горячие» методы. Есть ли способ использовать JIT даже после повторного преобразования? –
Я ожидаю некоторую частичную ситуацию с мертвой блокировкой. [Тупик агента JVMTI] (http://stackoverflow.com/questions/12291756/jvmti-agent-deadlock) –