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

时间:2017-04-07 13:26:25

标签: java c++ multithreading java-native-interface

我正在编写一个多线程C ++程序,它使用JNI与Jav​​a代码进行通信。根据设计,以下方法(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没有任何了解。

我的问题是如何在没有性能损失的情况下安全地分离线程是一个很好的解决方案?

2 个答案:

答案 0 :(得分:1)

最好的解决方案是仅在线程上附加一次并使其运行,并在线程存在时自动与线程本地存储(C ++ 11或更低版本)分离。 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;

}

答案 1 :(得分:0)

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

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

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