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的可能性。