是否有办法知道特定程序运行的最大JVM调用堆栈深度? [英] Is there a way to know maximally reached JVM call stack depth for a particular program run?

查看:68
本文介绍了是否有办法知道特定程序运行的最大JVM调用堆栈深度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天我一直在编码一个递归函数,递归深度取决于输入长度.

I've been coding one recursive function today and the recursion depth depends on the input length.

从纯粹的兴趣出发,我想知道是否有某种方法可以监视某个特定的程序执行过程中的最大调用堆栈深度,可能在某些JVM日志中或其他地方?

I was wondering from the pure interest point of view, is there some way to monitor, probably in some JVM logs or elsewhere, what has been the maximal call stack depth during a particular program execution?

经过一番思考,我可以想象出一种分析方法来近似地对此进行计算,但是这将是非常耗时的,并且需要对JVM内部和字节码有足够的了解.

After some thinking I can imagine an analytical approach to calculate this approximately but that would be very time-intensive and would require quite good knowledge of JVM internals and bytecode.

JVM允许配置堆栈大小内存的限制,但是我从未见过关于如何达到实际达到的限制的任何信息,而不是内存大小单位,而是分配的堆栈帧数.

JVM allows to configure the limit of a stack size memory, but I've never seen anything about how to get the actually reached limit and not in memory size units but the number of allocated stack frames.

推荐答案

一个可以轻松制作将跟踪

One can easily make JVMTI agent that will trace MethodEntry / MethodExit events and correspondingly increase or decrease stack depth counter. Here is an example of such agent. When the program ends, it will print the maximum recorded Java stack depth.

#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.

However, tracing each method entry and exit is very expensive. A less accurate, but much more efficient alternative would be a sampling profiler which periodically records a stack trace of a running thread, e.g. async-profiler or Java Flight Recorder.

这篇关于是否有办法知道特定程序运行的最大JVM调用堆栈深度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆