Problem
The JaCoP solver embeds a JVM (JNI_CreateJavaVM in solvers/jacop/java.cc) and keeps JNIEnv for the process lifetime. Local references are not freed when C++ functions return. They live until DeleteLocalRef / PopLocalFrame / DestroyJavaVM.
Model conversion creates one local ref per Java IntVar / constraint and never deletes it. Cached jclass values from FindClass are also stored as members without NewGlobalRef.
Hot path
solvers/jacop/jacop.cc MPToJaCoPConverter::Convert:
for (int j = 0; j < num_vars; ++j) {
jobject jvar = var_class_.NewObject(env_, store_, ...);
vars_[j] = jvar;
env_.SetObjectArrayElement(var_array_, j, jvar);
// missing: DeleteLocalRef(jvar) after the array holds it,
// or NewGlobalRef if vars_[j] must outlive the current frame
}
ClassBase::NewObject → Env::NewObjectV returns a fresh local ref on every call. The same pattern is used for constraints, NewObjectArray, and NewIntArray. Local-ref count scales with problem size.
SetObjectArrayElement / store.impose already keep the Java objects alive. The extra local refs are unnecessary.
Related issues in the JNI wrapper
solvers/jacop/java.h Class::DoInit:
class_ = env.FindClass(Info::name()); // local ref kept as a member
Init() runs once per Class<>, so this is a small fixed set, but it should still be a global ref. ClassBase::~ClassBase() does not release class_.
solvers/jacop/java.cc Env::NewObject(const char *class_name, ...) calls FindClass and never DeleteLocalRef on the jclass. Used a few times per solve (e.g. SimpleSelect), not in the variable loop.
The code already uses GlobalRef for search_ and obj_var, so the intended pattern exists; store_, vars_, and cached classes do not follow it.
Suggested fix
- After
SetObjectArrayElement / impose, DeleteLocalRef the temporary jobject (and temp arrays), or EnsureLocalCapacity / PushLocalFrame around Convert.
- Promote long-lived refs (
store_, vars_[i], cached class_) with NewGlobalRef; DeleteGlobalRef in the destructor.
- In
Env::NewObject(class_name, ...), DeleteLocalRef(cls) after NewObjectV.
Problem
The JaCoP solver embeds a JVM (
JNI_CreateJavaVMinsolvers/jacop/java.cc) and keepsJNIEnvfor the process lifetime. Local references are not freed when C++ functions return. They live untilDeleteLocalRef/PopLocalFrame/DestroyJavaVM.Model conversion creates one local ref per Java
IntVar/ constraint and never deletes it. Cachedjclassvalues fromFindClassare also stored as members withoutNewGlobalRef.Hot path
solvers/jacop/jacop.ccMPToJaCoPConverter::Convert:ClassBase::NewObject→Env::NewObjectVreturns a fresh local ref on every call. The same pattern is used for constraints,NewObjectArray, andNewIntArray. Local-ref count scales with problem size.SetObjectArrayElement/store.imposealready keep the Java objects alive. The extra local refs are unnecessary.Related issues in the JNI wrapper
solvers/jacop/java.hClass::DoInit:class_ = env.FindClass(Info::name()); // local ref kept as a memberInit()runs once perClass<>, so this is a small fixed set, but it should still be a global ref.ClassBase::~ClassBase()does not releaseclass_.solvers/jacop/java.ccEnv::NewObject(const char *class_name, ...)callsFindClassand neverDeleteLocalRefon thejclass. Used a few times per solve (e.g.SimpleSelect), not in the variable loop.The code already uses
GlobalRefforsearch_andobj_var, so the intended pattern exists;store_,vars_, and cached classes do not follow it.Suggested fix
SetObjectArrayElement/impose,DeleteLocalRefthe temporaryjobject(and temp arrays), orEnsureLocalCapacity/PushLocalFramearoundConvert.store_,vars_[i], cachedclass_) withNewGlobalRef;DeleteGlobalRefin the destructor.Env::NewObject(class_name, ...),DeleteLocalRef(cls)afterNewObjectV.