java-native-access/jna · warning

JNA: callback object has been garbage collected

Error message

JNA: callback object has been garbage collected

What it means

In invoke_callback, when the Java callback object has been garbage collected, JNA prints this warning to stderr, and if the callback has a non-void return type, zeroes the native return buffer. Native code still holds the trampoline, but the Java side object is unreachable, so the callback cannot be invoked.

Source

Thrown at native/callback.c:402

          }
        }
      }
    }
  }
  (*env)->ExceptionDescribe(env);
  (*env)->ExceptionClear(env);
  return 0;
}

static void
invoke_callback(JNIEnv* env, callback *cb, ffi_cif* cif, void *resp, void **cbargs) {
  jobject self;
  void *oldresp = resp;

  self = (*env)->NewLocalRef(env, cb->object);
  // Avoid calling back to a GC'd object
  if ((*env)->IsSameObject(env, self, NULL)) {
    fprintf(stderr, "JNA: callback object has been garbage collected\n");
    if (cif->rtype->type != FFI_TYPE_VOID) {
      memset(resp, 0, cif->rtype->size); 
    }
  }
  else if (cb->direct) {
    unsigned int i;
    void **args = alloca((cif->nargs + 3) * sizeof(void *));
    args[0] = (void *)&env;
    args[1] = &self;
    args[2] = &cb->methodID;
    memcpy(&args[3], cbargs, cif->nargs * sizeof(void *));

    // Note that there is no support for CVT_TYPE_MAPPER here
    if (cb->conversion_flags) {
      for (i=0;i < cif->nargs;i++) {
        switch(cb->conversion_flags[i]) {
        case CVT_INTEGER_TYPE:
        case CVT_POINTER_TYPE:

View on GitHub (pinned to d036ad9781)

Solutions

  1. Keep a strong reference to the callback object for as long as native code may invoke it (store it in a field or static collection).
  2. Call Native.unregister() (or dispose the library) when done so native no longer invokes stale trampolines.
  3. Treat the stderr warning as a bug indicator: audit callback lifetimes rather than suppressing it.

Example fix

// before
nativeLib.setCallback(new MyCallback()); // only referenced by native
// after
private MyCallback callback = new MyCallback(); // strong field reference
nativeLib.setCallback(callback);
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: A Java Callback object becomes unreachable and is GC'd while its native function pointer is still registered/called by native code — e.g. the callback was not kept in a Java field, or the library was not explicitly releases the callback via Native.unregister().

Common situations: Storing callbacks only in local variables while native code calls them asynchronously/later; long-lived native threads invoking callbacks after the creating Java scope exits.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/782cac523cab206f. Report an issue: GitHub.