{"record":{"id":"782cac523cab206f","repo":"java-native-access/jna","slug":"jna-callback-object-has-been-garbage-collected","errorCode":null,"errorMessage":"JNA: callback object has been garbage collected\n","messagePattern":"JNA: callback object has been garbage collected\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"native/callback.c","lineNumber":402,"sourceCode":"          }\n        }\n      }\n    }\n  }\n  (*env)->ExceptionDescribe(env);\n  (*env)->ExceptionClear(env);\n  return 0;\n}\n\nstatic void\ninvoke_callback(JNIEnv* env, callback *cb, ffi_cif* cif, void *resp, void **cbargs) {\n  jobject self;\n  void *oldresp = resp;\n\n  self = (*env)->NewLocalRef(env, cb->object);\n  // Avoid calling back to a GC'd object\n  if ((*env)->IsSameObject(env, self, NULL)) {\n    fprintf(stderr, \"JNA: callback object has been garbage collected\\n\");\n    if (cif->rtype->type != FFI_TYPE_VOID) {\n      memset(resp, 0, cif->rtype->size); \n    }\n  }\n  else if (cb->direct) {\n    unsigned int i;\n    void **args = alloca((cif->nargs + 3) * sizeof(void *));\n    args[0] = (void *)&env;\n    args[1] = &self;\n    args[2] = &cb->methodID;\n    memcpy(&args[3], cbargs, cif->nargs * sizeof(void *));\n\n    // Note that there is no support for CVT_TYPE_MAPPER here\n    if (cb->conversion_flags) {\n      for (i=0;i < cif->nargs;i++) {\n        switch(cb->conversion_flags[i]) {\n        case CVT_INTEGER_TYPE:\n        case CVT_POINTER_TYPE:","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/native/callback.c#L384-L420","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","solutions":["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).","Call Native.unregister() (or dispose the library) when done so native no longer invokes stale trampolines.","Treat the stderr warning as a bug indicator: audit callback lifetimes rather than suppressing it."],"exampleFix":"// before\nnativeLib.setCallback(new MyCallback()); // only referenced by native\n// after\nprivate MyCallback callback = new MyCallback(); // strong field reference\nnativeLib.setCallback(callback);","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store callback objects in long-lived fields or a static registry so GC cannot collect them","Call Native.unregister() when the callback is no longer needed","For async native calls, never use method-local callback instances","Monitor stderr for this warning in CI/native logs"],"tags":["jna","native","callback","garbage-collection"],"backgroundTag":"callback-garbage-collected","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}