{"record":{"id":"e0e76be91237b0a7","repo":"java-native-access/jna","slug":"jna-out-of-memory-can-t-allocate-local-frame","errorCode":null,"errorMessage":"JNA: Out of memory: Can't allocate local frame\n","messagePattern":"JNA: Out of memory: Can't allocate local frame\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"native/callback.c","lineNumber":745,"sourceCode":"      tls->needs_detach = needs_detach;\n      tls->jvm_thread = JNI_FALSE;\n    }\n    // Dispose of allocated memory\n    free((void *)args.name);\n    if (args.group) {\n      (*env)->DeleteWeakGlobalRef(env, args.group);\n    }\n  }\n\n  if (!tls) {\n    fprintf(stderr, \"JNA: couldn't obtain thread-local storage\\n\");\n    return;\n  }\n\n  // Give the callback glue 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  }\n  else {\n    invoke_callback(env, cb, cif, resp, cbargs);\n    // Make note of whether the callback wants to avoid detach\n    needs_detach = tls->needs_detach && !tls->jvm_thread;\n    (*env)->PopLocalFrame(env, NULL);\n  }\n  \n  if (needs_detach) {\n    if ((*jvm)->DetachCurrentThread(jvm) != 0) {\n      fprintf(stderr, \"JNA: could not detach thread\\n\");\n    }\n  }\n}\n\nconst char* \nJNA_callback_init(JNIEnv* env) {\n#ifdef PTHREADS","sourceCodeStart":727,"sourceCodeEnd":763,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/native/callback.c#L727-L763","documentation":"dispatch_callback pushes a JNI local frame (PushLocalFrame(env, 16)) so callback-local references are freed deterministically. If PushLocalFrame fails (returns < 0), it is an OutOfMemoryError condition; JNA prints this message but still calls invoke_callback, so local references created by the callback leak into the outer frame.","triggerScenarios":"PushLocalFrame returns negative because the JVM is out of memory — no native handle space remains to allocate the new local frame on the callback thread.","commonSituations":"Heap/native memory exhaustion during heavy callback traffic, a callback thread that leaked huge numbers of local references previously, tiny -Xmx with many attached threads.","solutions":["Free memory / increase -Xmx; verify the process is not near its native memory limit.","Fix any code path that leaks JNI local references (missing DeleteLocalRef) on callback threads.","Wrap callback dispatch with explicit PushLocalFrame/PopLocalFrame in your own native glue to bound reference growth.","Reduce callback frequency or batch work across fewer native->Java transitions."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Delete JNI local references promptly in native callback glue","Monitor JVM heap and native memory before heavy callback workloads","Avoid per-call attach/detach churn on callback threads","Size -Xmx to leave headroom for JNI handle allocation"],"tags":["jni","out-of-memory","local-frame","callback"],"backgroundTag":"jni-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"}