Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Blob] Release underlying resources when JS instance is GC'ed on Android #24767

Closed

Conversation

janicduplessis
Copy link
Contributor

Summary

Android followup for #24745. This adds a jsi object that removes blobs when it is gc'ed. We don't have many modules with native code on Android so I've added the native code directly in the blob package as a separate .so. I used a similar structure as the turbomodule package.

Changelog

[Android] [Fixed] - [Blob] Release underlying resources when JS instance is GC'ed on Android

Test Plan

Tested in RNTester by adding a breakpoint in the method that removes blobs and made sure it is triggered when making an http request with fetch.

@facebook-github-bot facebook-github-bot added Contributor A React Native contributor. CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels May 8, 2019
@janicduplessis janicduplessis force-pushed the blob-dealloc-android branch from 459d608 to 9fc56df Compare May 8, 2019 19:24
@react-native-bot react-native-bot added Platform: Android Android applications. Bug labels May 8, 2019
@janicduplessis janicduplessis force-pushed the blob-dealloc-android branch from 7b689fb to ccaf85f Compare May 8, 2019 20:52
@janicduplessis
Copy link
Contributor Author

I added the buck config but I can't confirm it works, RNTester currently crashes on master with 05-08 16:54:56.521 20634 20664 E SoLoader: couldn't find DSO to load: libreactnativejni.so when the app starts.

@janicduplessis janicduplessis force-pushed the blob-dealloc-android branch from ccaf85f to 1ee0449 Compare May 8, 2019 21:26
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hramos has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@cpojer cpojer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to land this.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpojer has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@react-native-bot
Copy link
Collaborator

This pull request was successfully merged by @janicduplessis in a4f7e17.

When will my fix make it into a release? | Upcoming Releases

@react-native-bot react-native-bot added the Merged This PR has been merged. label May 31, 2019
@cpojer
Copy link
Contributor

cpojer commented Jun 5, 2019

Heads up: this is causing an insta-crash in one of our apps. I'll try to get more details but we have to unland this change for now. In case you have a hunch of what could cause it, feel free to send the same PR again with a fix.

@janicduplessis
Copy link
Contributor Author

@cpojer Any change you can get me the crash log? I get a feeling it's related to fb internal setup.

@cpojer
Copy link
Contributor

cpojer commented Jun 10, 2019

Unfortunately I don't have anything actionable that I can share :(

@janicduplessis
Copy link
Contributor Author

@cpojer The crash happens in one specific fb app, not all apps right?

I can't tell for sure but most crashes I experienced when working on the feature was with JNI refs or failing to load the .so file, but those should lead to a clear stacktrace so might not be that.

https://github.com/facebook/react-native/pull/24767/files#diff-8d838f0df357a8f8620e924ba05c9815R16 maybe this could be null and cause an obscure crash later in c++.

What would be the best way test changes and try to move this forward?

@cpojer
Copy link
Contributor

cpojer commented Jun 10, 2019

@janicduplessis I'll put it on my queue and ask somebody at FB to help out re-land this.

@makovkastar
Copy link
Contributor

makovkastar commented Jun 28, 2019

@janicduplessis I found the reason of the crash in one of the FB apps, Proguard renamed the BlobModule.remove(...) method, so that the C++ couldn't find it:

auto removeMethod = jni::findClassStatic(kBlobModuleJavaDescriptor)->getMethod<void(jstring)>("remove");

I landed this diff by adding the @DoNotStrip annotation and it fixed the crash, but another app started crashing because jni::findClassStatic(kBlobModuleJavaDescriptor) cannot find this class.

The crash occurs in the debug build, so this time it's not related to Proguard apparently. It has something to do with the Java class loader probably. Do you have any ideas?

@janicduplessis
Copy link
Contributor Author

@makovkastar Hmm this is strange. Does this happen at launch, at a specific point or randomly later? We inject the __blobCollectorProvider function in the java BlobModule class so it has to exist at some point. I guess it's possible the class gets unloaded?

Do you know if there is something at facebook that allows native modules to be unloaded during runtime? In that case it is possible that the class gets unloaded if all instances are gone while the c++ code is still injected in the js runtime.

@makovkastar
Copy link
Contributor

makovkastar commented Jul 4, 2019

@janicduplessis Sorry, it took me some time to start working on this issue again. The crash happens always at the same time, shortly after the app is started. At first I thought it was because the destructor is not called from a Java thread, so JNI doesn't have access to the Java class loader (https://developer.android.com/training/articles/perf-jni#faq_FindClass). But by putting a breakpoint into the destructor of BlobCollector, I verified that it was actually called from a Java thread.

We can use ThreadScope::WithClassLoader to run code that has full access to Java as though you were running in a Java thread (in case you are in a non-Java thread and trying to use jni::findClass), so I tried to wrap the code in the destructor inside ThreadScope::WithClassLoader and it worked. Somehow even though the destructor is run on a Java thread, it doesn't have access to the class loader. This remains a mystery for me.

I don't think unloading classes is the case, since jni::findClassStatic cannot event find com/facebook/react/ReactActivity.

@janicduplessis
Copy link
Contributor Author

@makovkastar Nice find! I don't have much jni experience so I didn't know about this. Seems like a good fix to me.

I remember reading there was no guarantee on what thread jni objects destructors are called on (hence adding synchronization to BlobManager) but didn't about the difference between c++ and java threads.

@tlupo
Copy link

tlupo commented Jul 16, 2019

@makovkastar @janicduplessis Thank you very much for your work on this issue !

I work on a RN mobile application which has to make recurrent API calls and I had some memory issues after few hours of app usage (this app has to be active 24/7).

I have tested the fix for iOS on the new RN version (v0.60 : 9ef5107) and it works !

I would like to fix the Android version of the app (which seems to leak with new version). If I understand, it seems that the addition of « ThreadScope::WithClassLoader » in the destructor fixes the last side effect of this proposal ?

Do you think it’s ready to be used in production (I am looking for the best way to add this patch on my RN version, maybe via a fork, it’s gonna be hard to wait for the new RN version because this memory issue is critical for our app) ?

Again thank you very much for your help !

@janicduplessis
Copy link
Contributor Author

@tlupo Yes, I've been using this in prod for a few months without issues. 88e18b6 landed on master a few days ago which is the final version of this PR.

I'll post in the 0.60 release thread to see if we can get this cherry-picked in 0.60. You can also use this with a fork (note that you have to build android from source in this case https://github.com/facebook/react-native/wiki/Building-from-source).

@tlupo
Copy link

tlupo commented Jul 17, 2019

@janicduplessis Great ! I will try this PR ! Should be great to get it in RN 0.60. Thank you very much !

facebook-github-bot pushed a commit that referenced this pull request Sep 18, 2019
… 2 (#26155)

Summary:
Reland #24767

The commit had to be reverted because it caused a crash when using remote debugging in chrome. This is normal since jsi is not available in that environment. The crash was caused by `jsContext.get()` being 0, then being dereferenced later in c++. We can simply skip initializing the blob collector in this case.

This also includes the fix from #25720 to fix a crash when using hermes.

## Changelog

[Android] [Fixed] - Release underlying resources when JS instance is GC'ed on Android
Pull Request resolved: #26155

Test Plan:
Test using RN tester with jsc and hermes
Test remote debugging

Reviewed By: mdvacca, fred2028

Differential Revision: D17072644

Pulled By: makovkastar

fbshipit-source-id: 079d1d43501e854297fbbe586ba229920c892584
mcuelenaere pushed a commit to getdelta/react-native that referenced this pull request Sep 24, 2019
… 2 (facebook#26155)

Summary:
Reland facebook#24767

The commit had to be reverted because it caused a crash when using remote debugging in chrome. This is normal since jsi is not available in that environment. The crash was caused by `jsContext.get()` being 0, then being dereferenced later in c++. We can simply skip initializing the blob collector in this case.

This also includes the fix from facebook#25720 to fix a crash when using hermes.

[Android] [Fixed] - Release underlying resources when JS instance is GC'ed on Android
Pull Request resolved: facebook#26155

Test Plan:
Test using RN tester with jsc and hermes
Test remote debugging

Reviewed By: mdvacca, fred2028

Differential Revision: D17072644

Pulled By: makovkastar

fbshipit-source-id: 079d1d43501e854297fbbe586ba229920c892584
grabbou pushed a commit that referenced this pull request Oct 11, 2019
… 2 (#26155)

Summary:
Reland #24767

The commit had to be reverted because it caused a crash when using remote debugging in chrome. This is normal since jsi is not available in that environment. The crash was caused by `jsContext.get()` being 0, then being dereferenced later in c++. We can simply skip initializing the blob collector in this case.

This also includes the fix from #25720 to fix a crash when using hermes.

## Changelog

[Android] [Fixed] - Release underlying resources when JS instance is GC'ed on Android
Pull Request resolved: #26155

Test Plan:
Test using RN tester with jsc and hermes
Test remote debugging

Reviewed By: mdvacca, fred2028

Differential Revision: D17072644

Pulled By: makovkastar

fbshipit-source-id: 079d1d43501e854297fbbe586ba229920c892584
douglowder pushed a commit to react-native-tvos/react-native-tvos that referenced this pull request Dec 11, 2019
… 2 (#26155)

Summary:
Reland facebook/react-native#24767

The commit had to be reverted because it caused a crash when using remote debugging in chrome. This is normal since jsi is not available in that environment. The crash was caused by `jsContext.get()` being 0, then being dereferenced later in c++. We can simply skip initializing the blob collector in this case.

This also includes the fix from facebook/react-native#25720 to fix a crash when using hermes.

## Changelog

[Android] [Fixed] - Release underlying resources when JS instance is GC'ed on Android
Pull Request resolved: facebook/react-native#26155

Test Plan:
Test using RN tester with jsc and hermes
Test remote debugging

Reviewed By: mdvacca, fred2028

Differential Revision: D17072644

Pulled By: makovkastar

fbshipit-source-id: 079d1d43501e854297fbbe586ba229920c892584
M-i-k-e-l pushed a commit to M-i-k-e-l/react-native that referenced this pull request Mar 10, 2020
…cebook#24767)

Summary:
Android followup for facebook#24745. This adds a jsi object that removes blobs when it is gc'ed. We don't have many modules with native code on Android so I've added the native code directly in the blob package as a separate .so. I used a similar structure as the turbomodule package.

## Changelog

[Android] [Fixed] - [Blob] Release underlying resources when JS instance is GC'ed on Android
Pull Request resolved: facebook#24767

Differential Revision: D15279651

Pulled By: cpojer

fbshipit-source-id: 2bbdc4bbcbeae8945588ac5e3e895c49e6ac9e1a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Contributor A React Native contributor. Merged This PR has been merged. Platform: Android Android applications.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants