在性能路径中正确使用JNI DetachCurrentThread 我正在编写一个多线程的C++程序,它使用JNI与java代码通信。根据设计,以下方法由线程运行,运行一次后,本机线程可能会切换。循环式线程分配 bool JavaModule::run() { initObjects(); /*Attaching to the current thread *and checking for JVM exceptions *for each run */ Interpreter::getEnv()->CallObjectMethod(objid, msgid, NULL); if (Interpreter::getEnv()->ExceptionCheck()) { getLogger().error("ERR: JVM Exception occurred when running the script"); return false; } //Detaching from the current thread //There is a performance hit when detaching the Environment each time Interpreter::detachEnv(); return true; }

在性能路径中正确使用JNI DetachCurrentThread 我正在编写一个多线程的C++程序,它使用JNI与java代码通信。根据设计,以下方法由线程运行,运行一次后,本机线程可能会切换。循环式线程分配 bool JavaModule::run() { initObjects(); /*Attaching to the current thread *and checking for JVM exceptions *for each run */ Interpreter::getEnv()->CallObjectMethod(objid, msgid, NULL); if (Interpreter::getEnv()->ExceptionCheck()) { getLogger().error("ERR: JVM Exception occurred when running the script"); return false; } //Detaching from the current thread //There is a performance hit when detaching the Environment each time Interpreter::detachEnv(); return true; },java,c++,multithreading,java-native-interface,Java,C++,Multithreading,Java Native Interface,此调用位于程序的性能路径中,如果我尝试将环境与当前线程连接并分离,则会出现很大的性能问题。附件发生在getEnv中,如下所示 static JNIEnv* Interpreter::getEnv() { JNIEnv *env; int status = jvm->GetEnv((void**)&env, JNI_VERSION_1_6); if (status < 0) { status = jvm->AttachCur

此调用位于程序的性能路径中,如果我尝试将环境与当前线程连接并分离,则会出现很大的性能问题。附件发生在getEnv中,如下所示

static JNIEnv* Interpreter::getEnv()
{
    JNIEnv *env;
    int status = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (status < 0)
    {
        status = jvm->AttachCurrentThread((void**)&env, NULL);
        if (status < 0)
        {
            return nullptr;
        }
    }
    return env;
}
在这个代码级别,它不知道线程,在线程创建级别,它不知道JVM


我的问题是,在不影响性能的情况下,安全地分离线程的好解决方案是什么?

不要将性能关键型线程连接或分离到jvm。JVM需要与垃圾收集器同步,垃圾收集器是一个庞大的单线程关键部分

如果您需要一个性能关键线程来与jvm通信,那么您需要通过某种异步消息传递来实现


您还可以在线程创建和连接时附加和分离线程,但您仍然需要在其他方法中与GC同步。

< P>最好的解决方案是只附加一次线程并让它运行它的进程,并在线程存在时自动与线程本地存储C++ 11或IGTER分离。 JVM可以连接并保持多个线程,因此无需继续连接和分离。 下面是如何实现这一点的示例代码:

JNIEnv* JNIThreadHelper::GetJniEnv() {

// This method might have been called from a different thread than the one that created
// this handler. Check to make sure that the JNI is attached and if not attach it to the 
// new thread.

// double check it's all ok
int nEnvStat = m_pJvm->GetEnv(reinterpret_cast<void**>(&m_pJniEnv), JNI_VERSION_1_6);

if (nEnvStat == JNI_EDETACHED) {

    std::cout << "GetEnv: not attached. Attempting to attach" << std::endl;

    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6; // choose your JNI version
    args.name = NULL; // you might want to give the java thread a name
    args.group = NULL; // you might want to assign the java thread to a ThreadGroup

    if (m_pJvm->AttachCurrentThread(&m_pJniEnv, &args) != 0) {
        std::cout << "Failed to attach" << std::endl;
        return nullptr;
    }

    thread_local struct DetachJniOnExit {
        ~DetachJniOnExit() {
            m_pJvm->DetachCurrentThread();
        }
    };


    m_bIsAttachedOnAThread = true;

}
else if (nEnvStat == JNI_OK) {
    //
}
else if (nEnvStat == JNI_EVERSION) {

    std::cout << "GetEnv: version not supported" << std::endl;
    return nullptr;
}


return m_pJniEnv;

}

很难用设计创建异步模型。因为java程序是用C++代码调用的,上面的方法是它的起点。你不应该分离一个你没有明确附加的线程。也就是说,您需要跟踪您的JNIEnv*是来自GetEnv还是AttachCurrentThread,在后一种情况下只调用DetachCurrentThread。@Michael喜欢保留缓存并在进程结束时清理所有内容吗?我过去曾发布过一个关于此问题的答案:@Michael这似乎是一个不错的选择。我自己去试试看。非常感谢。请考虑你的答案,增加一些解释和细节。虽然它可能回答了这个问题,但仅仅添加一个链接并不能帮助OP或未来的社区成员理解这个问题或解决方案。
JNIEnv* JNIThreadHelper::GetJniEnv() {

// This method might have been called from a different thread than the one that created
// this handler. Check to make sure that the JNI is attached and if not attach it to the 
// new thread.

// double check it's all ok
int nEnvStat = m_pJvm->GetEnv(reinterpret_cast<void**>(&m_pJniEnv), JNI_VERSION_1_6);

if (nEnvStat == JNI_EDETACHED) {

    std::cout << "GetEnv: not attached. Attempting to attach" << std::endl;

    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6; // choose your JNI version
    args.name = NULL; // you might want to give the java thread a name
    args.group = NULL; // you might want to assign the java thread to a ThreadGroup

    if (m_pJvm->AttachCurrentThread(&m_pJniEnv, &args) != 0) {
        std::cout << "Failed to attach" << std::endl;
        return nullptr;
    }

    thread_local struct DetachJniOnExit {
        ~DetachJniOnExit() {
            m_pJvm->DetachCurrentThread();
        }
    };


    m_bIsAttachedOnAThread = true;

}
else if (nEnvStat == JNI_OK) {
    //
}
else if (nEnvStat == JNI_EVERSION) {

    std::cout << "GetEnv: version not supported" << std::endl;
    return nullptr;
}


return m_pJniEnv;