Я развернул простой агент JVMTI для тестирования инструментария байт-кода. Моя стратегия состоит в том, чтобы вызвать функцию RetransformClasses
в обратном вызове CompiledMethodLoad
, чтобы вызвать ClassFileLoadHook
. Для этого я написал следующий код:
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 выполнять горячие методы в интерпретаторе. - person Stanislav Lukyanov   schedule 26.12.20160.8
секундой и15
секундой. Это не похоже на затраты. Более того, даже если программа начнет выполняться в интерпретаторе, из-за режимаTieredCompilation
она должна снова вернуться в скомпилированный режим. Поправьте меня если я ошибаюсь. \\ На самом деле я хочу инструментировать только горячие методы. Можно ли как-нибудь использовать JIT даже после повторного преобразования? - person Saqib Ahmed   schedule 27.12.2016CompiledMethodLoad
? 2) Вы уверены, что переделываете только один класс? Вы специально гарантируете, что повторно преобразовываете класс, который хотите? Вам нужно будет, например, проверить имя объявляющего класса перед вызовом retransform. - person Stanislav Lukyanov   schedule 27.12.2016TieredCompilation
означает только наличие нескольких уровней компиляции. После того, как вы повторно преобразовали класс, VM должна выбросить весь скомпилированный для него код. Если вы повторно преобразовываете класс каждый раз, когда VM компилирует свой метод, он всегда отбрасывает скомпилированный код. Вы можете использовать JIT после повторного преобразования, вы просто не должны повторно преобразовывать один и тот же класс каждый раз, когда JIT что-то делает для вас. 4) Взаимная блокировка — это когда приложение не может продолжать работу из-за взаимной блокировки, а не когда оно работает медленно. Однако повторное преобразование должно выполнять некоторую синхронизацию, что также может быть причиной замедления. - person Stanislav Lukyanov   schedule 27.12.2016CompiledLoadMethod
. Код внутри блокаif
выполняется только один раз. Сообщите мне, если потребуется какая-либо другая информация. - person Saqib Ahmed   schedule 28.12.2016