使用JVMTI打印Java已加载类名时调用getName()方法崩溃问题
解决JNI+JVMTI调用Class.getName()崩溃问题
我用C++结合JNI和JVMTI实现打印目标Java应用所有已加载类名,但调用getName()方法时程序崩溃。已确认jvmtiEnv和Env初始化正常,其他方法可正常调用。
原代码
获取已加载类的代码:
static jvmtiEnv* jvmtiEnv; static JNIEnv* Env; // 以下代码位于某个方法内 jint class_count; jclass* classes_ptr; jvmtiEnv->GetLoadedClasses(&class_count, &classes_ptr); Logger::Log("LoadedClassCount: " + std::to_string(class_count)); for (int i = 0; i < class_count; i++) { jclass clazz = classes_ptr[i]; if (clazz) { //Logger::Log("Class Number: " + std::to_string(i)); jmethodID mid_getName = Env->GetMethodID(clazz, "getName", "()Ljava/lang/String;"); if (!mid_getName) continue; // 假设该方法并非总是可用 Logger::Log("Pass 2"); jobject classNameObj = Env->CallObjectMethod(clazz, mid_getName); // 崩溃发生在此处 if (!classNameObj) continue; Logger::Log("Pass 3"); // @Someprogrammerdude 建议的打印修正 std::string str = Env->GetStringUTFChars((jstring) classNameObj, 0); Logger::Log(("ClassName: " + str).c_str()); // 或 Logger::Log("ClassName: " + str) } }
日志方法代码:
void Logger::Log(std::string message) { if (!Logger::Initialized) Logger::Init(); std::cout << "[ LOG ] :: " + message << std::endl; std::ofstream log("C:\\Users\\me\\debug.txt", std::ofstream::app | std::ofstream::out); log << message << std::endl; }
问题原因
直接通过JNI调用Class.getName()存在以下风险:
- JVMTI返回的部分
jclass对象可能是弱引用或未绑定到当前JNI环境的特殊引用,直接调用方法会触发非法内存访问。 - 部分系统基础类在特定阶段未完成初始化,调用其方法会导致崩溃。
- 原代码中
GetStringUTFChars未调用ReleaseStringUTFChars,存在内存泄漏问题。
解决方法
改用JVMTI自带的GetClassSignature或GetClassNameAPI获取类名,无需依赖JNI调用Java方法,稳定性更高。
修正后的代码:
static jvmtiEnv* jvmtiEnv; static JNIEnv* Env; // 以下代码位于某个方法内 jint class_count; jclass* classes_ptr; jvmtiError get_classes_err = jvmtiEnv->GetLoadedClasses(&class_count, &classes_ptr); if (get_classes_err != JVMTI_ERROR_NONE) { Logger::Log("Failed to get loaded classes: " + std::to_string(get_classes_err)); return; } Logger::Log("LoadedClassCount: " + std::to_string(class_count)); for (int i = 0; i < class_count; i++) { jclass clazz = classes_ptr[i]; if (clazz) { char* class_name = nullptr; // 使用JVMTI获取类签名,包含完整类路径 jvmtiError get_name_err = jvmtiEnv->GetClassSignature(clazz, &class_name, nullptr); if (get_name_err == JVMTI_ERROR_NONE && class_name != nullptr) { Logger::Log("ClassName: " + std::string(class_name)); // 释放JVMTI分配的字符串,避免内存泄漏 jvmtiEnv->Deallocate((unsigned char*)class_name); } // 也可以使用GetClassName获取更简洁的类名 // jvmtiError get_name_err = jvmtiEnv->GetClassName(clazz, &class_name, nullptr); } } // 释放JVMTI分配的类数组 jvmtiEnv->Deallocate((unsigned char*)classes_ptr);
额外提示:原代码中GetLoadedClasses返回的classes_ptr需要调用Deallocate释放,否则会造成内存泄漏,修正后的代码已补充此步骤。
内容的提问来源于stack exchange,提问作者UnSure
相关产品推荐
相关产品推荐

