Repository navigation
fix(android): free runtime-owned JNI refs and method callback data on dispose - #92
Open
NathanWalker wants to merge 1 commit into
Open
NathanWalker wants to merge 1 commit into
NathanWalker wants to merge 1 commit into
Conversation
… dispose Disposing a runtime (a worker, or a React Native guest runtime) left three things behind: - ObjectManager's LRU cache of Java objects: ~LRUCache drops its map without calling the evict callback, so every cached weak global ref stayed in the process-wide weak reference table. Add LRUCache::clear(), which evicts each entry, and call it from ObjectManager's dispose. - The JNI global ref ~Runtime holds on the Java com.tns.Runtime. - MethodCallbackData for static methods, extension-only methods and methods added from a skipped base class. CtorFuncCache only owns the instance method data, so these were never deleted (about 180 per worker that touches a handful of classes). MetadataNodeCache now owns them and deletes them in onDisposeEnv/onDisposeRuntime. Applied to both the napi and jsi trees. Split out of #89.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
This was referenced Oct 25, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Split out of #89 at the reviewer's request. This PR has the leak fixes only, with no attach/detach changes.
Summary
Disposing a runtime left three things behind. Today that is a worker; once #89 lands it is also a React Native guest runtime.
LRU cache weak refs.
~LRUCachedrops its map without evicting, so every cached weak global ref to a Java object stayed in the process-wide weak reference table. AddedLRUCache::clear(), which evicts each entry, andObjectManagercalls it at dispose.Java
Runtimeglobal ref.~Runtimenow deletes the JNI global ref it holds oncom.tns.Runtime.MethodCallbackData.CtorFuncCacheowns only instance-method data. The data for these was never deleted:MetadataNodeCache::methodCallbackDatanow owns it, andonDisposeEnv/onDisposeRuntimedelete it.Applied to both the napi and jsi trees.
Why this can't double free
Every allocation site registers its pointer with exactly one owner:
CtorFuncCacheentry (unchanged)CtorFuncCacheentry (unchanged)methodCallbackData(new; only entries not ininstanceMethodData)methodCallbackData(new; registered right afternew)SetMissingBaseMethods, new datamethodCallbackData(new). A match borrowed frominstanceMethodDatais not registered againCtorFuncCachehas no duplicates. Its list for a class holds exactly that class's owninstanceMethodData:GetConstructorFunctionInternaltakes the vector by value, so it always starts empty.parent, which owns nothing.FunctionInfo.Evidence
Pixel 9 emulator (API 36, arm64), standalone test app.
terminate(), 40×close()from inside the worker, and 12 concurrent terminates. Each worker used static, extension-only and instance methods.Known and unchanged: on jsi + QuickJS, a worker that calls
close()from inside its message handler after using Java classes aborts inJS_FreeRuntime(list_empty(&rt->gc_obj_list)). The base branch does the same.Not verified:
Merge Danger
Door: two-way
Blast Radius: Android runtime dispose: worker
terminate/close, and RN detach once #89 landsThe newly owned callback data is freed at the same point as the instance-method data already was. So code that calls a native method on a disposed runtime fails the same way for static methods as it already did for instance methods.