在性能路径中正确使用 JNI DetachCurrentThread

Properly using JNI DetachCurrentThread in performance paths

本文关键字:JNI DetachCurrentThread 性能 路径      更新时间:2023-10-16

我正在编写一个多线程C++程序,它使用JNI与Java代码进行通信。根据设计,以下方法(run(((由线程运行,运行一次后,本机线程可能会切换。(循环式线程分配(

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;
}

此调用位于程序的性能路径中,如果我尝试从当前线程附加和分离环境,则会出现很大的性能问题。附件发生在 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 是一个定义为 static JavaVM* jvm;

分离代码如下所示。

static bool Interpreter::detachEnv()
{
    if (jvm->DetachCurrentThread() == JNI_OK)
    {
        return true;
    }
    return false;
}
在这个代码级别,它不知道线程,

在线程创建级别,它对JVM一无所知。

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

最好的解决方案是只附加到线程一次,让它运行它的过程,并在线程存在时自动与线程本地存储(C++ 11 或 ighter(分离。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;

}

不要将

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

如果您需要一个性能关键线程来与 jvm 通信,则需要使用某种异步消息传递来完成。

您也可以在线程创建和连接时附加和分离线程,但您仍然必须在其他方法中与 gc 同步。