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

Java 21迁移后JNI CallStaticObjectMethod触发访问违例崩溃求助

Java 21迁移后JNI调用CallStaticObjectMethod触发Access Violation崩溃问题

我们将系统从Java 8迁移至Java 21并更新JNI版本后,所有JNI调用在Java 8下正常,但调用CallStaticObjectMethod触发Access Violation异常崩溃,仅该方法出现问题,其余JNI方法运行正常。

崩溃的C++调用代码

jobject Bindings::ToSystemArchObj(JNIEnv* env, const std::string& arch)
{
    if (env == nullptr || arch == ARCH_UNSUPPORTED)
    {
        return nullptr;
    }
    jobject jstr = env->NewStringUTF(arch.c_str()); // 已尝试将jstr类型从jobject转为jstring

    processArchObj = env->CallStaticObjectMethod(JniProcessInfoDTO::SystemArchClass, JniProcessInfoDTO::ofMethodId, jstr); // 该行触发访问违例并崩溃

    return processArchObj;
}

JNI类与方法ID初始化代码

void JniProcessInfoDTO::initClass(JNIEnv* env)
{
    jclass const localProcessInfoDTO = env->FindClass(JavaTypes::ProcessInfoDTO.c_str());
    ProcessInfoDTORef = reinterpret_cast<jclass>(env->NewGlobalRef(localProcessInfoDTO));
    initFieldIDs(env);
    // std::string const SystemArch = "Lcom/company/mfw/model/dto/SystemArch;";
    SystemArchClass = env->FindClass(JavaTypes::SystemArch.c_str());
    
    // (Ljava/lang/String;)Lcom/company/mfw/model/dto/SystemArch;
    const std::string ofSign = "(" + JavaTypes::String + ")" + JavaTypes::SystemArch; //// std::string const String = "java/lang/String;";

    ofMethodId = env->GetStaticMethodID(SystemArchClass, "of", ofSign.c_str());
}

Java端静态方法(SystemArch枚举)

package com.company.mfw.model.dto;

public enum SystemArch {

    X86, X86_64, UNSUPPORTED;

    public static SystemArch of(String arch) {
        try {
            return SystemArch.valueOf(arch);
        } catch (Exception ignored) {
            return UNSUPPORTED;
        }
    }

}

JNI加载与卸载逻辑

extern "C"
{
    // cppcheck-suppress unusedFunction
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*)
    {
        // This is an entry point for initialization of Java classes.
        // All required Java classes must exist and must be initialized here.
        // JNI_OnLoad must not be wrapped with Safe Environment.
        // JVM should be crashed in case of exceptions here.
        
        JniGlobals::JVMConnector::InitJavaVM(vm);

        JavaVMAttachArgs args;
        args.version = JNI_VERSION_21;
        args.name = nullptr;
        args.group = nullptr;
        JNIEnv* env = nullptr;

        vm->AttachCurrentThread((void**)&env, &args);
        JniContext::Current().JniEnv = env;
        JniGlobals::unmaskFPSR();

        GdiplusStartup(&JniGlobals::gdiplusToken, &JniGlobals::gdiplusStartupInput, nullptr);

        Bindings::JniComponentDTO::initClass(env);
        Bindings::JniWindowDTO::initClass(env);
        Bindings::JniPointDTO::initClass(env);
        Bindings::JniDimensionDTO::initClass(env);
        Bindings::JniRectangleDTO::initClass(env);
        Bindings::JniProcessInfoDTO::initClass(env); 
        Bindings::JniProcessStartupInfoDTO::initClass(env);
        Bindings::JniProcessEndInfoDTO::initClass(env);
        Bindings::JniKeyDTO::initClass(env);
        Bindings::JniNativeMouseEvent::initClass(env);
        Bindings::JniNativeKeyEvent::initClass(env);
        Bindings::JniHooksSettings::initClass(env);
        Bindings::WinSessionDTO::initClass(env);

        JniGlobals::TimeManager::GetInstance().InitJavaHandlers(env);

        JniGlobals::JavaObjectFactory::JavaObjectFactoryClass = env->FindClass("com/company/mfw/bridge/internal/JavaObjectsFactory");
        JniGlobals::JavaObjectFactory::NewStringMethodID
            = env->GetStaticMethodID(JniGlobals::JavaObjectFactory::JavaObjectFactoryClass, "newString", "([B)Ljava/lang/String;");

        Log::GetInstance().Init();

        LOG_TRACE(L"BEGIN");
        auto osInfo = JniGlobals::WindowsSystemInfo::GetInstance().ReadOsInfo();
        std::string csd(osInfo.szCSDVersion);
        LOG_INFO(L"System info. Version: {}.{}, Build: {}, Platform ID: {}, CSD Version: {}", (int)osInfo.dwMajorVersion,
                 (int)osInfo.dwMinorVersion, (int)osInfo.dwBuildNumber, (int)osInfo.dwPlatformId,
                 csd.length() > 0 ? StringsConverter::StringToWstring(csd) : L"<empty>");

        return JNI_VERSION_21;
    }

    // cppcheck-suppress unusedFunction
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM*, void*)
    {
        LOG_TRACE(L"BEGIN");

        Gdiplus::GdiplusShutdown(JniGlobals::gdiplusToken);

        JNIEnv* env = JniContext::Current().JniEnv;
        Bindings::JniComponentDTO::releaseClass(env);
        Bindings::JniWindowDTO::releaseClass(env);
        Bindings::JniPointDTO::releaseClass(env);
        Bindings::JniDimensionDTO::releaseClass(env);
        Bindings::JniRectangleDTO::releaseClass(env);
        Bindings::JniProcessInfoDTO::releaseClass(env);
        Bindings::JniProcessStartupInfoDTO::releaseClass(env);
        Bindings::JniProcessEndInfoDTO::releaseClass(env);
        Bindings::JniKeyDTO::releaseClass(env);
        Bindings::JniNativeMouseEvent::releaseClass(env);
        Bindings::JniNativeKeyEvent::releaseClass(env);
        Bindings::JniHooksSettings::releaseClass(env);
        Bindings::WinSessionDTO::releaseClass(env);
    }

} // extern "C"

补充上下文(更新2)

C++类型转换调用代码

/**
 * Converts a ProcessInfoPtr to a Java ProcessInfoDTO object.
 *
 * @param env The JNI environment pointer.
 * @param pi The ProcessInfoPtr object.
 * @return The corresponding Java ProcessInfoDTO object or nullptr if the environment or ProcessInfoPtr is null.
 */
jobject Bindings::ToJavaType(JNIEnv* env, ProcessInfoPtr pi)
{
    if (env == nullptr || pi == nullptr)
    {
        return nullptr;  // Return nullptr if environment or ProcessInfoPtr is null
    }

    jobject joProcessInfoDTO = env->AllocObject(JniProcessInfoDTO::ProcessInfoDTORef);  // Allocate a new ProcessInfoDTO object

    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processPidFieldId, pi->pid);  // Set process PID field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processParentPidFieldId, pi->parentPid);  // Set process parent PID field

    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processChildProcessFieldId, ToJavaType(env, pi->childProcesses));  // Set child processes field

    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processCommandLineFieldId, ToJavaType(env, pi->commandLine));  // Set command line field
    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processNameFieldId, ToJavaType(env, pi->name));  // Set process name field
    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processOwnerFieldId, ToJavaType(env, pi->owner));  // Set process owner field

    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processResidentBytesFieldId, pi->residentBytes);  // Set resident bytes field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processTotalBytesFieldId, pi->totalBytes);  // Set total bytes field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processSystemMillsFieldId, pi->systemMillis);  // Set system milliseconds field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processUserMillsFieldId, pi->userMillis);  // Set user milliseconds field

    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processArchFieldId, ToSystemArchObj(env, pi->arch));  // Set process architecture field

    return joProcessInfoDTO;  // Return the ProcessInfoDTO object
}

C++ ProcessInfo类定义

class ProcessInfo
{
public:
    ProcessInfo()
        : pid(-1), parentPid(0), commandLine(L""), name(L""), owner(L""), residentBytes(0), totalBytes(0), systemMillis(0), userMillis(0),
          arch("")
    {
        childProcesses.resize(0);
    };

    ~ProcessInfo() = default;

public:
    int pid;
    int parentPid;
    std::vector<int> childProcesses;
    std::wstring commandLine;
    std::wstring name;
    std::wstring owner;

    long residentBytes;
    long totalBytes;
    long systemMillis;
    long userMillis;

    std::string arch;
};

Java ProcessInfoDTO类部分定义

public class ProcessInfoDTO {

    private static final long serialVersionUID = -212324578578245L;

    private int pid;
    private int parentPid;
    private String commandLine;
    private String name;
    private String owner;
    private int[] childProcesses;

    private long residentBytes;
    private long totalBytes;
    private long systemMillis;
    private long userMillis;

    private SystemArch arch;

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }
    // 更多函数定义
    // 更多函数定义
}

排查方向

  1. 局部引用未转为全局引用:SystemArchClass是FindClass返回的局部引用,在JNI_OnLoad线程外使用会失效。Java 21对局部引用的生命周期管理更严格,需调用NewGlobalRef保存:

    // 修改initClass中的SystemArchClass初始化
    jclass localSystemArch = env->FindClass(JavaTypes::SystemArch.c_str());
    SystemArchClass = reinterpret_cast<jclass>(env->NewGlobalRef(localSystemArch));
    env->DeleteLocalRef(localSystemArch);
    

    同时要在releaseClass中释放全局引用:

    void JniProcessInfoDTO::releaseClass(JNIEnv* env) {
        env->DeleteGlobalRef(SystemArchClass);
        // 其他释放逻辑
    }
    
  2. 方法签名错误:检查JavaTypes::String和JavaTypes::SystemArch的定义是否正确。正确的签名应该是:

    • String: "Ljava/lang/String;"
    • SystemArch: "Lcom/company/mfw/model/dto/SystemArch;"
      确保拼接后的签名是"(Ljava/lang/String;)Lcom/company/mfw/model/dto/SystemArch;",可通过javap -s com.company.mfw.model.dto.SystemArch验证。
  3. JNI环境有效性:调用ToSystemArchObj时,当前线程是否已正确附着到JVM?检查env指针是否有效,可在调用前添加判断:

    if (env == nullptr) {
        // 重新附着线程获取env
        JavaVM* vm = JniGlobals::JVMConnector::GetJavaVM();
        if (vm != nullptr) {
            JavaVMAttachArgs args;
            args.version = JNI_VERSION_21;
            args.name = nullptr;
            args.group = nullptr;
            jint result = vm->AttachCurrentThread((void**)&env, &args);
            if (result != JNI_OK) {
                return nullptr;
            }
        } else {
            return nullptr;
        }
    }
    
  4. 字符串编码问题:Java 21对字符串编码的处理可能更严格,NewStringUTF要求输入是UTF-8编码,确保arch字符串是合法UTF-8,可尝试转换为宽字符串后用NewString:

    std::wstring warch = StringsConverter::StringToWstring(arch);
    jstring jstr = env->NewString(warch.c_str(), warch.length());
    

内容的提问来源于stack exchange,提问作者Stew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:29:55