In general, Botje is right, don't use the finalize
method along with JNI.
But if you still need to finalize
then don't use jweak
variables created with NewWeakGlobalRef
.
Comment from djinni project:
We don't use JNI WeakGlobalRef objects, because they last longer than
is safe - a WeakGlobalRef can still be upgraded to a strong reference
even during finalization, which leads to use-after-free. Java WeakRefs
provide the right lifetime guarantee.
If you can't do without weak references, use java.lang.ref.WeakReference
. WeakReference
objects in finilize
must return null
.
A simple C++ WeakReference
wrapper:
struct GlobalRefDeleter {
void operator()(jobject globalRef) noexcept {
if (globalRef != nullptr) {
if (JNIEnv* env = getEnv()) {
env->DeleteGlobalRef(globalRef);
}
}
}
};
template <typename PointerType>
class GlobalRef : public std::unique_ptr<typename std::remove_pointer_t<PointerType>, GlobalRefDeleter> {
using Parent = std::unique_ptr<typename std::remove_pointer_t<PointerType>, GlobalRefDeleter>;
public:
GlobalRef() = default;
GlobalRef(JNIEnv* env, PointerType localRef) : Parent(static_cast<PointerType>(env->NewGlobalRef(localRef)), GlobalRefDeleter{}) {
}
bool isSame(JNIEnv* env, jobject obj) const {
return env->IsSameObject(obj, Parent::get());
}
};
struct WeakReferenceJniInfo {
GlobalRef<jclass> clazz;
jmethodID constructor;
jmethodID get;
explicit WeakReferenceJniInfo(JNIEnv* env)
: clazz{env, env->FindClass("java/lang/ref/WeakReference")},
constructor{env->GetMethodID(clazz.get(), "<init>", "(Ljava/lang/Object;)V")},
get{env->GetMethodID(clazz.get(), "get", "()Ljava/lang/Object;")} {
}
};
// created in JNI_OnLoad
inline std::unique_ptr<const WeakReferenceJniInfo> g_weakReferenceJniInfo;
class WeakRef {
public:
WeakRef() = default;
WeakRef(JNIEnv* env, jobject obj)
: _weakRef(std::make_shared<GlobalRef<jobject>>(
env, env->NewObject(g_weakReferenceJniInfo->clazz.get(), g_weakReferenceJniInfo->constructor, obj))) {
}
// Get the object pointed to if it's still strongly reachable or, return null if not.
// (Analogous to weak_ptr::lock.) Returns a local reference.
jobject lock(JNIEnv* env) const {
if (_weakRef == nullptr) {
return nullptr;
}
else {
return env->CallObjectMethod(_weakRef->get(), g_weakReferenceJniInfo->get);
}
}
bool isSame(JNIEnv* env, jobject obj) const {
auto local = lock(env);
auto result = env->IsSameObject(obj, local);
env->DeleteLocalRef(local);
return result;
}
private:
std::shared_ptr<GlobalRef<jobject>> _weakRef;
};