Java 删除对象分配JNI调用API中的jobject引用

Java 删除对象分配JNI调用API中的jobject引用,java,c++,reference,java-native-interface,invocation,Java,C++,Reference,Java Native Interface,Invocation,我要执行以下循环: for(absolute_date CURRENT_DATE = START_DATE; CURRENT_DATE.COMPARE_TO(END_DATE) <= 0; CURRENT_DATE.SHIFT_SELF(TIMESTEP)) { CURRENT_STATE = Propagator.PROPAGATE(CURRENT_DATE); } 为了更详细地描述我所面临的问题:propagator::PROPAGATE()方法按值返回一个状态对象(当前为堆

我要执行以下循环:

for(absolute_date CURRENT_DATE = START_DATE; CURRENT_DATE.COMPARE_TO(END_DATE) <= 0; CURRENT_DATE.SHIFT_SELF(TIMESTEP))
{
    CURRENT_STATE = Propagator.PROPAGATE(CURRENT_DATE);
}
为了更详细地描述我所面临的问题:
propagator::PROPAGATE()
方法按值返回一个
状态
对象(当前为堆栈分配)。一旦此函数返回,就会发生以下情况:

1) 调用移动分配运算符。这将设置
jobject\u状态
jclass\u状态
对象中的成员


2) 为在PROPAGATE()函数中创建的
状态
实例调用析构函数。这将删除对jobject_state的本地引用,因此当前_state对象不再具有有效的成员变量。

从何处开始。。。JNI是一个非常挑剔和不宽容的人,如果你不把事情做好,它就会爆炸。你的描述很简单(如果没有帮助,请提供更多细节),但我可以做出一个很好的猜测。你的方法有几个问题。你大概在做这样的事情:

state::state(JNIEnv* ENV)
{
    this->ENV = ENV;
    this->jclass_state = ENV->FindClass("/path/to/class");
    this->jobject_state = nullptr;                                                  
}

state::~state()
{
    if(DOES_JVM_EXIST())
    {
        ENV->DeleteLocalRef(this->jclass_state); 
        ENV->DeleteLocalRef(this->jobject_state); //PROBLEMATIC
    }
}

state::state(state&& state_to_move)
{
    this->ENV = state_to_move.ENV;

    //move jobjects from mover => new object
    this->jobject_state = state_to_move.jobject_state;
    this->jclass_state = state_to_move.jclass_state;
}

state& state::operator =(state&& state_to_move)
{
    this->ENV = state_to_move.ENV;

    //move jobjects from mover => current object
    this->jobject_state= state_to_move.jobject_state;
    this->jclass_state = state_to_move.jclass_state;
    return *this;
} 
struct state {
   state(jobject thing_) : thing(thing_) {}
   ~state() { env->DeleteLocalRef(thing); }
   jobject thing;
}
第一个问题是存储本地引用是危险的。您不能在当前JNI框架之外继续使用它们。因此,将其转换为全局:

struct state {
   state(jobject thing_) : thing(env->NewGlobalRef(thing_)) { 
      env->DeleteLocaLRef(thing_); 
   }
   ~state() { env->DeleteGlobalRef(thing); }
   jobject thing;
}
第二个问题是,Joobe基本上就像旧的C++ AutoPPTR——确实不安全,因为复制它会导致挂起的指针和双释放。因此,您要么需要禁止复制state,要么只传递state*,要么创建一个有效的复制构造函数:

state(const state& rhs) thing(env->NewGlobalRef(rhs.thing)) {}
这至少会让你走上正轨

更新:关于本地引用与全局引用,Ddor对此进行了很好的描述:“当执行从创建本地引用的本机方法返回时,本地引用将无效。因此,本机方法不得存储本地引用并期望在后续调用中重用它。”您可以保留本地引用,但必须在严格的情况下。特别要注意的是,您不能将这些线程交给另一个线程,而您似乎没有这样做。另一件事——可以活动的本地引用的总数是有限制的。令人沮丧的是,这个限制没有被明确规定,但它似乎是特定于JVM的。我建议谨慎行事,并始终转换为全球战略

我以为我在某个地方读到过,您不需要删除jclass,因为FindClass()总是返回相同的内容,但我很难验证这一点。在我们的代码中,我们总是将jclass转换为全局引用

ENV->DeleteLocalRef(this->jclass_state); 
我必须承认对C++迁移语义的无知;只需确保没有调用默认的copyctor,并且没有两次释放jobject_状态

this->jobject_state = state_to_move.jobject_state;
如果调用的是move构造函数而不是copy构造函数或赋值,我不知道为什么会在销毁临时文件时看到delete。正如我所说,我不是移动语义学方面的专家。我总是让复制构造函数创建一个新的全局。参考。

您不能这样做:

this->ENV = ENV;
您正在缓存从JVM传递给本机代码的
JNIEnv

你不能那样做

好吧,说得迂腐一点,有些情况下你可以这样做,但这只能从一个线程开始工作,所以当同一个线程使用
JNIEnv*
值时,不需要缓存该值供以后参考

从您发布的内容的复杂性来看,我严重怀疑您能否保证您的本机代码每次都被同一线程调用

每次从JVM调用本机代码时,都会传递一个
JNIenv*
,因此缓存
JNIenv
值几乎没有任何意义


依我看,您的本机代码太复杂了。不需要缓存和跟踪所有这些引用。看起来,您试图保持与C++对象同步的java对象。为什么?如果本机代码需要访问Java对象,只需在从Java到本机代码的调用中传递该对象。

HI Wheezil,谢谢您的评论。请参见上面的编辑您的移动构造函数/赋值运算符需要将
状态\u to \u move
中的本地引用设置为
NULL
,因为
状态\u to \u move
不再拥有这些引用的所有权。当然,析构函数需要能够处理这些引用为NULL的可能性。