chore: reproduce BundlerDownload OOM issue#52797
Closed
AndreiCalazans wants to merge 1 commit into
Closed
Conversation
Collaborator
|
Contributor
I think that, because this is related to |
Contributor
Author
Got it. I'll follow up with the issue tomorrow. |
Contributor
Author
|
Issue #52818 |
4 tasks
Collaborator
|
This PR is stale because it has been open for 180 days with no activity. It will be closed in 7 days unless you comment on it or remove the "Stale" label. |
Collaborator
|
This PR was closed because it has been stalled for 7 days with no activity. |
Contributor
Author
|
superseded by #57043 |
meta-codesync Bot
pushed a commit
that referenced
this pull request
Jun 2, 2026
Summary: During JS bundle downloads from Metro, the multipart stream reader was copying each chunk into a new Buffer before passing it to listeners. For large bundles, this resulted in elevated peak memory usage due to duplicating chunk data (Okio read buffer + intermediate Buffer copy, plus downstream buffering), which can exceed emulator heap limits for large bundles. Example: #52818 Repro: #52797 ### Changes - Multipart parsing: pass a **bounded** `BufferedSource` per part (prevents reading past the part into the next boundary) and **drain unread bytes** after callbacks so listeners don’t need to fully consume the body. - BundleDownloader: keep streaming download behavior while restoring **atomic writes** (`.tmp` + rename) to avoid partial bundles on interruption. - Make Content-Type checks tolerant of parameters, parse `X-Http-Status` safely. ## Changelog: [ANDROID] [FIXED] - Reduced memory usage during JS bundle downloads by eliminating intermediate buffer copies Pull Request resolved: #54854 Test Plan: # From OSS contributor - [x] Verified multipart bundle downloads work correctly with progress callbacks displayed - [x] Verified non-multipart fallback path still functions - [x] Verified error responses are handled correctly - [x] Tested with large bundles (repro above) and confirmed reduced memory pressure and no crashes # E2E With Playground app (D102154809) and profiling {F1988731912} ### Memory regression test (Android) Built a scripted A/B harness for `BundleDownloader.downloadBundleFromURL` using an RNTester playground native module that exposes the downloader to JS and to `adb shell am broadcast`. Per iteration: force-stop the app, relaunch, capture `VmHWM` from `/proc/<pid>/status` as the pre-download high-water mark, fire the broadcast, wait for a `DOWNLOAD_COMPLETE` logcat sentinel, capture `VmHWM` and `dumpsys meminfo` again. Compared this commit against its parent (`abf4662924`) on a Samsung Galaxy S22 (`SM-S901B`, 256 MB default Java heap). **OOM regression test.** With a 200 MB synthetic JS bundle and the default 256 MB Java heap, the parent commit deterministically `OutOfMemoryError`s in `okio.Segment.<init>` from `MultipartStreamReader.readAllParts`. With this commit applied, the same download completes successfully. To get a numerical comparison at all I had to add `android:largeHeap="true"` and shrink the test bundle to 80 MB so the parent variant could finish. **Steady-state memory** — 80 MB bundle, 5 iterations per variant, `largeHeap=true`: | Variant | Avg post-download `VmHWM` | Avg delta from pre | Worst-case delta | |---|---|---|---|---| | After | 395 MB | +84 MB | +91 MB | | Before | 394 MB | +103 MB | **+196 MB** | `progressEvents = 0` for every parent-commit run confirms the parent code was the variant under test: its `headers["Content-Type"] == "application/javascript"` exact-match filter never fires against Metro's `application/javascript; charset=utf-8`. This patch tolerates the `; charset=…` parameter and emits ~150 events per download. **Per-iteration data** ``` phase iter pre_vmhwm_kb post_vmhwm_kb delta_kb duration_ms progress_events with-fix 1 313344 396796 83452 4386 149 with-fix 2 310344 388944 78600 4252 136 with-fix 3 313428 397092 83664 4602 150 with-fix 4 306804 398272 91468 4358 150 with-fix 5 309196 391764 82568 4410 149 without-fix 1 312332 393780 81448 3922 0 without-fix 2 196304 392668 196364 4621 0 without-fix 3 317616 393640 76024 4486 0 without-fix 4 314836 397812 82976 4362 0 without-fix 5 313224 392808 79584 3795 0 ``` **Existing tests.** `buck2 test fbsource//xplat/js/react-native-github/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport:devsupport_MultipartStreamReaderTestAndroid` passes, including the new `testListenerDoesNotNeedToFullyReadBody` and `testHeaderNamesAreCaseInsensitive` cases that cover the bounded `BufferedSource` listener contract and case-insensitive header lookup. Reviewed By: GijsWeterings Differential Revision: D93102028 Pulled By: robhogan fbshipit-source-id: ec63d31cc6d72d2d4d852578072d810d3a54218d
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.
Summary:
The whole point of this PR is to reproduce this OOM issue we are having with large bundles on Android emulators and start a dicussion on if having this land in RN Core is somethign that interests the team before I put any effort in migrating the current Java patch to the latest Kotlin version of both BundleDownloader and MultipartStreamReader.
cc @cortinico does this need to be an issue or can we discuss on the PR?
Context
We are facing this OOM on Android emluators due to our large bundles. While we can reduce the size of the bundles this seems to keep cropping back up as it grows again or when the emulator gets bottlenecked by other memory usages. We were able to fix the problem by patching the BundleDownloader with a bit of help from LLMs.
We were able to fix it by patching the BundleDownloader and MultipartStreamReader on version 0.77.2 which was still the Java version. I didn't want to put any effort towards migrating this to Kotlin unless RN Core team says this is ok to land in RN Core.
Changelog:
Test Plan: