Есть ли способ узнать максимально достигнутую глубину стека вызовов JVM для конкретного запуска программы? - PullRequest
0 голосов
/ 05 сентября 2018

Сегодня я кодировал одну рекурсивную функцию, и глубина рекурсии зависит от длины ввода.

Мне было интересно с точки зрения чистого интереса, есть ли какой-нибудь способ мониторинга, возможно, в некоторых журналах JVM или где-либо еще, какова максимальная глубина стека вызовов во время выполнения конкретной программы?

Подумав немного, я могу представить себе аналитический подход для приблизительного расчета этого, но он потребует очень много времени и потребует достаточно хороших знаний внутренних компонентов JVM и байт-кода.

JVM позволяет настроить ограничение объема памяти стека, но я никогда не видел ничего о том, как получить фактически достигнутый предел, и не в единицах измерения объема памяти, а в количестве выделенных кадров стека.

1 Ответ

0 голосов
/ 06 сентября 2018

Можно легко создать агент JVMTI, который будет отслеживать MethodEntry / MethodExit событий и соответственно увеличивать или уменьшать счетчик глубины стека. Вот пример такого агента. Когда программа завершится, она напечатает максимальную записанную глубину стека Java.

#include <jvmti.h>
#include <stdint.h>
#include <stdio.h>

static volatile int max_depth = 0;

static int adjust_stack_depth(jvmtiEnv *jvmti, int delta) {
    intptr_t depth = 0;
    (*jvmti)->GetThreadLocalStorage(jvmti, NULL, (void**)&depth);
    (*jvmti)->SetThreadLocalStorage(jvmti, NULL, (const void*)(depth + delta));
    return (int)depth;
}

void JNICALL MethodEntry(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method) {
    adjust_stack_depth(jvmti, +1);
}

void JNICALL MethodExit(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
                        jboolean was_popped_by_exception, jvalue return_value) {
    int depth = adjust_stack_depth(jvmti, -1);
    if (depth > max_depth) {
        max_depth = depth;  // TODO: replace with atomic CAS to avoid race condition
    }
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    jvmtiEnv* jvmti;
    (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);

    jvmtiCapabilities capabilities = {0};
    capabilities.can_generate_method_entry_events = 1;
    capabilities.can_generate_method_exit_events = 1;
    (*jvmti)->AddCapabilities(jvmti, &capabilities);

    jvmtiEventCallbacks callbacks = {0};
    callbacks.MethodEntry = MethodEntry;
    callbacks.MethodExit = MethodExit;
    (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));

    (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, NULL);
    (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, NULL);

    return 0;
}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
    printf("Max stack depth = %d\n", max_depth);
}

Компиляция:

gcc -fPIC -shared -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -o libmaxdepth.so maxdepth.c

Пробег:

java -agentpath:/path/to/libmaxdepth.so MyProgram

Однако отслеживание входа и выхода каждого метода очень дорого. Менее точной, но гораздо более эффективной альтернативой будет профилировщик выборки, который периодически записывает трассировку стека работающего потока, например, async-profiler или Java Flight Recorder.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...