我一直在摆弄JVMTI API,尝试写一些有用的东西。将来自其他SO问题的代码粘贴在一起,我试图创建一个简单的工具,当发生某种特殊类型的异常时,它会转储堆,这可能在创建jdb监听器很难的环境中很有用。提到的代码:
#include <jni.h>
#include <jvmti.h>
#include <string.h>
#include <stdio.h>
#include "jmm.h"
JNIEXPORT void* JNICALL JVM_GetManagement(jint version);
jvmtiEnv* jvmti;
void JNICALL
Exception(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jobject exception,
jmethodID catch_method,
jlocation catch_location) {
char *exception_signature;
/* Obtain signature of the exception and compare type name with FNFE */
jclass class = (*jni_env)->GetObjectClass(jni_env, exception);
(*jvmti)->GetClassSignature(jvmti_env, class, &exception_signature, NULL);
if (strcmp("Ljava/io/FileNotFoundException;", exception_signature)==0) {
JmmInterface* jmm = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);
if (jmm == NULL) {
printf("Sorry, JMM is not supported\n");
} else {
jstring path = (*jni)->NewStringUTF(jni, "dump.hprof");
jmm->DumpHeap0(jni, path, JNI_TRUE);
printf("Heap dumped\n");
}
}
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = Exception;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL)
return 0;
}
这样的过程正在发挥作用;我在一个简单的测试程序上尝试这个代码时得到一个堆转储(打印几行并在它们之间生成异常)。问题是,Eclipse MAT中的线程堆栈分析显示了使用DestroyJavaVM而不是main方法的堆栈跟踪,这与使用jmap生成的堆转储不同。我希望堆转储与jmap不同,因为它是一个JNI调用'中断'已检测线程的正常执行,但不会破坏整个跟踪,只是本机调用位于堆栈顶部。
为什么会这样,我能以某种方式阻止这个问题,并以我喜欢的方式转储堆吗?或者我的整个推理是错误的,这是不可能的?