アレクサンダーArendar:
今日は1つの再帰関数をコーディングしてきたと再帰の深さは入力の長さに依存します。
私は、特定のプログラムの実行中に最大コールスタックの深さとなっているものを、いくつかのJVMは、ログや他の場所おそらくでは、モニターにはいくつかの方法があり、ビューの純粋な関心ポイントから不思議でしたか?
いくつかの思考の後、私は約これを計算するための分析的なアプローチを想像することができますが、それは非常に時間のかかるだろうし、JVMの内部とバイトコードのかなり良い知識を必要とします。
JVMはスタックサイズのメモリの制限を設定することができますが、私は実際には達して制限はなく、メモリサイズの単位ではなく、割り当てられたスタックフレームの数を取得する方法について何も見たことがありません。
apangin:
一つは、簡単にトレースする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
しかし、各メソッドの入口と出口を追跡することは非常に高価です。それほど正確ではなく、はるかに効率的な代替手段は、定期的に実行中のスレッド、例えばのスタックトレースを記録サンプリングプロファイラだろう非同期プロファイラまたはJavaフライトレコーダーを。