You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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()存在以下风险:

  1. JVMTI返回的部分jclass对象可能是弱引用或未绑定到当前JNI环境的特殊引用,直接调用方法会触发非法内存访问。
  2. 部分系统基础类在特定阶段未完成初始化,调用其方法会导致崩溃。
  3. 原代码中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 02:20:01