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

Purge children from view registry when UIManager is invalidated #38617

Conversation

kkafar
Copy link
Contributor

@kkafar kkafar commented Jul 25, 2023

Summary:

Talking about Paper & iOS here.

In standard RN applications when a native component is removed permanently from view hierarchy it is invalidated (if it implements RCTInvalidating). Components that implement RCTInvalidating such as RNSScreenView of react-native-screens library rely on RCTInvalidating#invalidate method being called in adequate moment to release retained resources (in my case the RNSScreenView holds a strong reference to it's view controller preventing it from being garbage collected).

However in case of brownfield applications (React Native is used only for a particular view & loaded on demand, see: software-mansion/react-native-screens#1754 for discussion & app example) when view controller holding RCTRootView is dismissed and whole React Native managed view / controller tree gets deallocated, RCTInvalidating#invalidate method is not called on the dismissed components, thus in my particular use case, leading to memory leak.

Right now I've added call to RCTUIManager#_purgeChildren:fromRegistry: (which internally invalidates all components which implement RCTInvalidating) in RCTUIManager#invalidate.

Changelog:

[IOS][FIXED] - Purge children from view registry on RCTUIManager invalidation.

Test Plan:

You can run the demo provided in the issue.

Following screenshots show that memory leak in brownfield application is resolved.

Without the change (invalidate method is not being called on native components)

image

With the change:

image

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Jul 25, 2023
@analysis-bot
Copy link

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,843,439 -4
android hermes armeabi-v7a 8,152,598 +0
android hermes x86 9,349,234 -1
android hermes x86_64 9,191,954 -2
android jsc arm64-v8a 9,456,861 -2
android jsc armeabi-v7a 8,637,995 -2
android jsc x86 9,539,948 -2
android jsc x86_64 9,783,247 -1

Base commit: e64756a
Branch: main

@hoxyq
Copy link
Contributor

hoxyq commented Oct 5, 2023

Hey @kkafar, thanks for opening this PR! Could you please rebase and check if CI tests are passing?

Also, can someone who is more familiar with this part take a look?
cc @javache @sammy-SC

@kkafar kkafar force-pushed the @kkafar/invalidate-view-registry-when-invalidating-ui-manager branch from 02d13ec to e8a2aa7 Compare October 5, 2023 10:34
@kkafar
Copy link
Contributor Author

kkafar commented Oct 5, 2023

Yeah, sure, done.
Waiting for CI.

@hoxyq hoxyq requested review from javache and sammy-SC October 5, 2023 10:41
@kkafar
Copy link
Contributor Author

kkafar commented Oct 5, 2023

Hey, CI fails but it does not look like my change affects it -- rather some kind of build issue.

@javache
Copy link
Member

javache commented Oct 5, 2023

Seems reasonable. I'm surprised we don't have another codepath which does this.

One other way you could get the right behaviour here is to make sure you stop each root before you tear down React Native, that will cause all views to be unmounted and also will allow useEffect destructors to run.

@javache
Copy link
Member

javache commented Oct 5, 2023

Can you rebase? The pod error should've been resolved by ce39931

@facebook-github-bot
Copy link
Contributor

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

@kkafar kkafar force-pushed the @kkafar/invalidate-view-registry-when-invalidating-ui-manager branch from e8a2aa7 to a4d75dc Compare October 6, 2023 06:56
@kkafar
Copy link
Contributor Author

kkafar commented Oct 6, 2023

@javache, yeah sure, rebased

@facebook-github-bot
Copy link
Contributor

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

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Oct 11, 2023
@facebook-github-bot
Copy link
Contributor

@javache merged this pull request in bc63e44.

lunaleaps pushed a commit that referenced this pull request Nov 17, 2023
Summary:
Talking about Paper & iOS here.

In standard RN applications when a native component is removed permanently from view hierarchy [it is invalidated (if it implements `RCTInvalidating`)](https://github.com/facebook/react-native/blob/e64756ae5bb5c0607a4d97a134620fafcb132b3b/packages/react-native/React/Modules/RCTUIManager.m#L483-L495). Components that implement `RCTInvalidating` such as [`RNSScreenView`](https://github.com/software-mansion/react-native-screens/blob/9fb3bd00850bcdf29b46daa57e56eabda3ae30ea/ios/RNSScreen.mm#L35) of [`react-native-screens`](https://github.com/software-mansion/react-native-screens) library rely on `RCTInvalidating#invalidate` method being called in adequate moment to release retained resources (in my case the `RNSScreenView` holds a strong reference to it's view controller preventing it from being garbage collected).

However in case of brownfield applications (React Native is used only for a particular view & loaded on demand, see: software-mansion/react-native-screens#1754 for discussion & app example) when view controller holding `RCTRootView` is dismissed and whole `React Native` managed view / controller tree gets deallocated, `RCTInvalidating#invalidate` method is not called on the dismissed components, thus in my particular use case, leading to memory leak.

Right now I've added call to `RCTUIManager#_purgeChildren:fromRegistry:` (which internally invalidates all components which implement `RCTInvalidating`) in `RCTUIManager#invalidate`.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[IOS][FIXED] - Purge children from view registry on `RCTUIManager` invalidation.

Pull Request resolved: #38617

Test Plan:
You can run the [demo](https://github.com/mkondakov/RNSScreensMemoryLeak) provided in the [issue](software-mansion/react-native-screens#1754).

Following screenshots show that memory leak in brownfield application is resolved.

Without the change (`invalidate` method is not being called on native components)

![image](https://github.com/facebook/react-native/assets/50801299/dac331c2-1e7c-4e66-a8c1-b88f7a007d9b)

With the change:

![image](https://github.com/facebook/react-native/assets/50801299/7a8afbe9-446c-47a2-a972-d7589b921677)

Reviewed By: NickGerleman

Differential Revision: D49952215

Pulled By: javache

fbshipit-source-id: 6336b86774615acc40279c97e6ae0bb777bda8ad
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants