Repository navigation
fix(android): release JS errors handed to Java instead of leaking a handle per throw - #131
Open
ammarahm-ed wants to merge 2 commits into
Open
ammarahm-ed wants to merge 2 commits into
ammarahm-ed wants to merge 2 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
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.
Port of the
NativeScriptException::m_javascriptExceptionitem of NativeScript/android#2013 to both the Node-API and the direct runtime.When a JS error crosses into Java (a JS callback throws and a Java frame is above it),
ReThrowToJavagave the JavaNativeScriptExceptionthe address of the engine handle as ajlong. Java has nothing to free it with, and the handle is only released if the exception comes back to JS andWrapJavaToJsExceptionconverts it. An exception that Java catches and drops therefore pinned itsErrorand captured stack for the life of the process: anapi_refin the Node-API runtime, a heap-allocatedJsValuein the direct one.Java now receives an id instead of an address, into a table owned by
NativeScriptException(TransferToJava/TakeFromJava/ReleaseTransferred):rt.identity(), because theengine::Runtimewrapper object is not the same on every call.A weak JNI reference per entry, to drop an entry when its throwable is collected, would be the more precise lifetime, but a burst of 400,000 throws aborted the app inside ART's
AddWeakGlobalRef: Android caps weak global references at about 51,200 per process.No Java change is needed (
jsValueAddressstays along). The first commit adds the benchmark case that throws from a JS callback and is swallowed in Java.Results
V8 release build on a Sony SO-01M (Android 11, arm64). Suite case: 50,000 throws, 5 launches, median.
At 50,000 throws the leak is below this device's memory noise. At 400,000 throws (a one-off build that runs only the throwing case, Node-API, 3 launches, not committed):
So the handle leak is real and this removes about 300 MB of it, at no cost per throw. It does not remove all of the growth: about 900 MB of native heap remains after 400,000 throws, roughly 2 KB per throw once the roughly 100 MB baseline is excluded, and I have not found where it comes from. It is not the handle this PR frees.
The Jasmine suite on a debug build of the top of this stack, same device:
triggers interval, a timing spec)That includes
TestThrowJSExceptionThroughJavaAndCatchInJS, which checks that the same JS object comes back through five nested Java frames. It failed on the direct runtime while the table was keyed by runtime address and passes withrt.identity().The 400,000-throw comparison was not run on the direct runtime. Only V8 was built and run.