Skip to content

JaCoP JNI: local refs from model conversion are never released (Invocation API) #283

Description

@QiuYucheng2003

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::NewObjectEnv::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

  1. After SetObjectArrayElement / impose, DeleteLocalRef the temporary jobject (and temp arrays), or EnsureLocalCapacity / PushLocalFrame around Convert.
  2. Promote long-lived refs (store_, vars_[i], cached class_) with NewGlobalRef; DeleteGlobalRef in the destructor.
  3. In Env::NewObject(class_name, ...), DeleteLocalRef(cls) after NewObjectV.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions