几个星期以来我遇到了一个问题,我需要知道我的直觉是否正确。我的 Android 应用程序正在使用 c++ 库,并且每隔一段时间就会触发一次 SIGSEGV 错误。
我有一个用 C++ 编写的 NetworkThread,它从服务器接收更新对象。我有一个用 Java 编写的 WorkerThread,它询问 NetworkThread 是否每 0.5 秒有新的更新。
我使用 SWIG 生成的 JNI 包装器在 c++ 和 Java 之间进行通信。
在 NetworkThread 中,我们有一个std::vector
包含所有新更新的内容。
WorkerThread (Java) 使用这一行来获取一个新的 Update 对象:
Update u = nwt.nextUpdate();
然后在 NetworkThread (c++) 中触发以下代码:
Update NetworkThread::nextUpdate() {
pthread_mutex_lock(&nwt_mutex);
pthread_mutex_lock(&inQ_mutex); // RACING CONDITION
Update c = inQ.front();
inQ.erase(inQ.begin());
if (inQ.size() <= QUEUE_SIZE) {
pthread_cond_broadcast(&inQ_threshold_cv);
}
pthread_mutex_unlock(&inQ_mutex);
pthread_mutex_unlock(&nwt_mutex);
return c;
}
该行Update c = inQ.front();
制作对象的浅表副本(更新不会覆盖 = 运算符)。我认为这很糟糕,因为 Update 包含对其他对象(和对象向量)的引用。
然后inQ.erase(inQ.begin());
调用该行,从 std::vector 的文档中删除的元素被销毁。这是否意味着此时对 c 中的对象和向量的引用不再有效?
使用此 JNI 片段将 Update 对象发送回 Java 后:
// Retrieve the current JNIEnv* with the cached JVM
int status;
JNIEnv* env;
bool isAttached = false;
status = gCachedJVM->GetEnv((void **) &env, JNI_VERSION_1_2);
if(status < 0) {
status = gCachedJVM->AttachCurrentThread(&env, NULL);
if(status < 0) {
return;
}
isAttached = true;
}
jmethodID update = env->GetMethodID(gClazzUpdateListenerWrapper, "update", "(J)V"); // J stands for Java long type
// Call Java method update from jUpdateListener object
env->CallVoidMethod(jUpdateListener, update, (jlong)(intptr_t)&u); // Pointer as agument, we'll build the Update object in Java
if (isAttached) {
gCachedJVM->DetachCurrentThread();
}
在这里,我向 Java 发送一个地址(长),然后像这样构建对象(我使用 SWIG 生成的默认构造函数):
Player p1;
public void update(long ptrUpdate) {
final Update u = new Update(ptrUpdate, false);
p1 = u.getEntity(0).toPlayer();
...
}
像我一样这样做是不好的做法吗?我认为用 long 这样做是不好的,但是如果地址已分配并且有效,那么应该没有任何问题吧?
无论如何,这似乎工作得很好,直到我收到一个 SIGSEGV 错误(没有更多信息),我怀疑这是因为我一直使用对已被销毁和收集的对象的引用。