小编Mal*_*ins的帖子

从本机线程通过JNI回调时,Java线程泄漏

简介:在本机创建的线程上从本机代码调用Java时,我看到Java线程泄漏.

(2014年2月11日更新:我们将此作为对Oracle的支持请求提出.现在Oracle 已经在Java 7更新45上证实了它.它只影响64位Linux(可能还有Mac)平台:32位Linux不受影响) .

(2014年4月29日更新:Oracle已解决此问题,并将在Java 7更新80中发布).

我有一个由Java层和本机库组成的应用程序.Java层通过JNI调用本机库:这会导致新的本机线程开始运行,后者将调用回Java.由于新的本机线程未附加到JVM,因此需要在执行回调之前将其附加,然后再分离.通常的方法是使用AttachCurrentThread/DetachCurrentThread调用将回调到Java的代码括起来.这样可以正常工作,但对于我们的应用程序(非常频繁地调用Java),每次附加和分离的开销都很大.

在几个地方(比如这里这里)描述了一个优化,它建议使用基于线程本地存储的机制来消除这个问题:基本上每次触发本机回调时,都会测试线程是否已经附加到JVM:如果没有,它将附加到JVM,并且线程本地存储机制用于在线程退出时自动分离线程.我已经实现了这一点,但是虽然附加和分离似乎正确发生,但这会导致Java端的线程泄漏.我相信我正在做正确的事情,并且正在努力寻找可能出错的地方.我一直在抨击这个问题一段时间,我会非常感谢任何见解.

我已经以减少的形式重新创建了问题.下面是本机层的代码.我们这里有一个包装器,它封装了为当前线程返回JNIEnv指针的过程,使用POSIX线程本地存储机制自动分离线程(如果它尚未连接).有一个回调类充当Java回调方法的代理.(我已经使用回调到静态Java方法,以消除创建和删除对Java对象的全局对象引用的额外复杂性,这与此问题无关).最后有一个JNI方法,在调用时,构造一个回调,并创建一个新的本机线程并等待它完成.这个新创建的线程调用回调一次然后退出.

#include <jni.h>
#include <iostream>
#include <pthread.h>


using namespace std;


/// Class to automatically handle getting thread-specific JNIEnv instance,
/// and detaching it when no longer required
class JEnvWrapper
{

public:

    static JEnvWrapper &getInstance()
    {
        static JEnvWrapper wrapper;
        return wrapper;
    }

    JNIEnv* getEnv(JavaVM *jvm)
    {
        JNIEnv *env = 0;
        jint result = jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
        if (result != JNI_OK)
        {
            result = jvm->AttachCurrentThread((void **) …
Run Code Online (Sandbox Code Playgroud)

c++ java java-native-interface multithreading callback

55
推荐指数
1
解决办法
4695
查看次数