{"record":{"id":"b9567a25ea615211","repo":"java-native-access/jna","slug":"jna-out-of-memory-can-t-allocate-local-frame-dispatch","errorCode":null,"errorMessage":"JNA: Out of memory: Can't allocate local frame","messagePattern":"JNA: Out of memory: Can't allocate local frame","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"native/dispatch.c","lineNumber":2073,"sourceCode":"closure_handler(ffi_cif* cif, void* resp, void** argp, void *cdata)\n{\n  callback* cb = (callback *)cdata;\n  JavaVM* jvm = cb->vm;\n  JNIEnv* env;\n  jobject obj;\n  int attached = (*jvm)->GetEnv(jvm, (void *)&env, JNI_VERSION_1_4) == JNI_OK;\n\n  if (!attached) {\n    if ((*jvm)->AttachCurrentThread(jvm, (void *)&env, NULL) != JNI_OK) {\n      fprintf(stderr, \"JNA: Can't attach native thread to VM for closure handler\\n\");\n      return;\n    }\n  }\n\n  // Give the callback its own local frame to ensure all local references\n  // are properly disposed\n  if ((*env)->PushLocalFrame(env, 16) < 0) {\n    fprintf(stderr, \"JNA: Out of memory: Can't allocate local frame\");\n  }\n  else {\n    obj = (*env)->NewLocalRef(env, cb->object);\n    if ((*env)->IsSameObject(env, obj, 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    else {\n      (*env)->CallVoidMethod(env, obj, MID_ffi_callback_invoke,\n                             A2L(cif), A2L(resp), A2L(argp));\n    }\n\n    (*env)->PopLocalFrame(env, NULL);\n  }\n\n  if (!attached) {\n    if ((*jvm)->DetachCurrentThread(jvm) != 0) {","sourceCodeStart":2055,"sourceCodeEnd":2091,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/native/dispatch.c#L2055-L2091","documentation":"Before dispatching a native callback invocation, JNA pushes a JNI local frame (PushLocalFrame, capacity 16) so all local references created during the callback are disposed at once. If PushLocalFrame fails (native out of memory), JNA prints this message to stderr and skips the normal dispatch path — the Java callback is not invoked and the native response buffer handling is bypassed.","triggerScenarios":"A JNA Callback fires while the JVM/native heap is exhausted, so (*env)->PushLocalFrame(env, 16) returns a negative value.","commonSituations":"Native memory exhaustion in long-running services with many threads attached via JNI; JNI local-reference pressure in deeply recursive native-to-Java callback chains; containers with undersized RAM limits.","solutions":["Free native memory / increase available RAM or native heap for the process (e.g. container memory limits, -Xmx interplay with native allocations).","Reduce depth/frequency of nested native-to-Java callback chains that hold local frames.","Profile with JNI local reference tracking (-verbose:jni, -Xcheck:jni) to find leaks keeping frames alive.","Add out-of-memory monitoring and graceful degradation so callbacks are unregistered before memory pressure becomes fatal."],"exampleFix":"// before: unbounded native thread pool each attaching to JVM\nfor (int i = 0; i < 100000; i++) new Thread(nativeCallbackTask).start();\n// after: bounded pool\nExecutorService pool = Executors.newFixedThreadPool(16);\nfor (Task t : tasks) pool.submit(nativeCallbackTask);","handlingStrategy":"fallback","validationCode":"// Check headroom before registering many callback threads:\nlong freeNative = ((com.sun.jna.Pointer) null) != null ? Runtime.getRuntime().freeMemory() : 0;\nif (Runtime.getRuntime().freeMemory() < 64L * 1024 * 1024) {\n  throw new IllegalStateException(\"Insufficient memory headroom for callback registration\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Bound the number of threads that attach to the JVM from native code.","Set container/JVM memory limits with native-heap overhead in mind, not just -Xmx.","Enable -Xcheck:jni during tests to expose local-reference pressure before it becomes OOM."],"tags":["jni","jna","native","out-of-memory","callbacks"],"backgroundTag":"out-of-memory","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"}