You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
🐛 [Android] Each FrameOutput leaks its native thread (dispose() doesn't shut down the executor; useFrameOutput never disposes) #4213
install simple app on an Android device or emulator
Open the camera screen (uses useFrameOutput with a trivial onFrame that disposes the frame)
Toggle the camera off and on
After each cycle run: adb shell ps -T -p $(adb shell pidof ) | grep -c camera.frame
What did you expect to happen?
Closing the camera frees its frame thread. The count stays at 1 while the camera is open and 0 when it's closed.
What actually happened?
The count goes up by one every time the camera opens and never goes down. In our production app, in a test session reached 42 threads, and memory kept growing with each camera open. There's no crash; the app gets slower until it freezes.
Affected platforms
Android (device)
Device(s) affected
Android, Samsung s23e, Samsung galaxy AO5 and A07, Motorola G35
VisionCamera version
5.1.1
React Native version
0.86.3
React Native architecture
New Architecture (Fabric / bridgeless)
Features being used
Preview
Photo capture
Video capture
Frame Processors (worklets)
Skia Frame Processors
Code/Barcode Scanner
Location metadata
Multi-cam
Depth data
HDR / custom dynamic range
Custom format / FPS / resolution
Relevant logs / stack trace
# VisionCamera 5.1.1, Android 14 emulator (API 34, x86_64)# logs captured after the app froze following repeated camera screen opens.# No error or warning is logged by VisionCamera/CameraX; the leak is silent in logcat.
total threads: 235, "lo.camera.frame" threads: 42 (all S / futex_wait)
16143 S 27465 futex_wait com.pdvmundo
16618 S 56 futex_wait lo.camera.frame
16784 S 84 futex_wait lo.camera.frame
16956 S 123 futex_wait lo.camera.frame
17063 S 139 futex_wait lo.camera.frame
17153 S 111 futex_wait lo.camera.frame
17260 S 115 futex_wait lo.camera.frame
... (36 more, same state)
21752 S 88 futex_wait lo.camera.frame
21918 S 103 futex_wait lo.camera.frame
Additional context
No response
Submission
The reproduction I linked is either (preferred) a PR against this repo that adds a failing harness test following the harness-tests README, or (fallback) a public repo that reproduces the bug on a fresh clone. I understand the issue will be closed without one.
I pasted logs as text (not screenshots).
I wrote this report in my own words. I did not paste AI-generated descriptions of the bug.
I have a fix for this on Android in #4227. The frame thread now exits after 5 s without a frame callback, and useFrameOutput removes its callbacks on unmount. On a Pixel 8 Pro, 10 mount and unmount cycles left 10 camera.frame threads before and 0 after a 7 s hold. The PR includes the #4212 test.
@dmandrade, since your app reached 42 leaked threads: could you try the branch from #4227 (chocky335:android/frame-output-executor-leak) and watch adb shell ps -T -p <pid> | grep camera.frame after a few screen changes? With the branch, that count returns to zero about 5 s after the last frame processor stops. If it stays up in your app, the logcat around that moment would help me.
We ported PR #4227 (890f5da) onto react-native-vision-camera@5.1.1 + react-native-vision-camera-worklets@5.1.1 with patch-package (staying off 5.2.x because of Nitro 0.37).
Ran the ps -T protocol you asked for on a Pixel_API_36 emulator, same process (com.pdvmundo pid 23191) across 10 camera open/close cycles:
Count never grew 1, 2, 3… A new camera.frame thread appears per session and the previous one is gone. After the last close, count is 0. Pid stayed 23191 the whole time (no crash / process restart).
Cycle 3 still had 1 thread at +7 s (state R); the next idle window it dropped to 0 — delayed idle, not a leak. No logcat captured at that instant because the following cycle already showed 0.
Photo smoke on the same process: first shutter did capture (PHOTO_ORIENT capture_raw, 1856×1392), several captures in a row, no ANR / FATAL / process death. PSS still ratchets (~835 MB → ~1.07 GB over extra sessions; ~1.09 → 1.23 GB across the capture burst) — that is a separate native-memory issue, not this thread leak.
We do not have the <4 GB Samsung in hand for this run; we will repeat ps -T there when we do.
Follow-up on a 3 GB emulator cold boot (Pixel_API_36, hw.ramSize=3G, MemTotal 3047 MB), same patched 5.1.1 build:
ps -T | grep camera.frame over 10 open/close cycles in pid 4580: 1 while open, 1 just after close, 0 at ~7 s. Thread ids changed each session; count never stacked.
Photo smoke: first two shutters captured (capture_raw). The process then disappeared (pid gone, JS dropped). The emulator itself stayed up; MemAvailable recovered to ~1.69 GB. So the thread leak looks fixed even under 3 GB; the remaining kill is the native PSS ratchet, not leftover camera.frame threads.
Thanks @guuhgalvao, this is exactly the check I needed: one new camera.frame thread per session and none left after close, also on 3 GB. The PSS growth you see is outside this fix. #4227 only changes the thread lifetime, and a process kill with no camera.frame threads left points at native memory that is not freed. A separate issue with a heap profile (adb shell dumpsys meminfo <pid> across sessions, or a Perfetto native heap trace) would give @mrousavy something to work with.
Activity
chocky335 commentedon Oct 19, 2026
I have a fix for this on Android in #4227. The frame thread now exits after 5 s without a frame callback, and
useFrameOutputremoves its callbacks on unmount. On a Pixel 8 Pro, 10 mount and unmount cycles left 10camera.framethreads before and 0 after a 7 s hold. The PR includes the #4212 test.chocky335 commentedon Oct 20, 2026
@dmandrade, since your app reached 42 leaked threads: could you try the branch from #4227 (
chocky335:android/frame-output-executor-leak) and watchadb shell ps -T -p <pid> | grep camera.frameafter a few screen changes? With the branch, that count returns to zero about 5 s after the last frame processor stops. If it stays up in your app, the logcat around that moment would help me.dmandrade commentedon Oct 20, 2026
Sure @chocky335! I'll test our app against the #4227 branch, repeat the same flow, and report back.
guuhgalvao commentedon Oct 29, 2026
We ported PR #4227 (
890f5da) ontoreact-native-vision-camera@5.1.1+react-native-vision-camera-worklets@5.1.1with patch-package (staying off 5.2.x because of Nitro 0.37).Ran the
ps -Tprotocol you asked for on a Pixel_API_36 emulator, same process (com.pdvmundopid23191) across 10 camera open/close cycles:| cycle | while open | ~1 s after close | ~7 s after close | thread id |
|------:|-----------:|-----------------:|-----------------:----------:|
| 1 | 1 | 1 | 0 | 23362 → (gone) |
| 2 | 1 | 1 | 0 | 23572 |
| 3 | 1 | 1 | 1 (still
R) | 23660 || 4 | 1 | 1 | 0 | 23660 (exited) |
| 6 | 1 | 1 | 0 | 24831 |
| 7 | 1 | 1 | 0 | 24929 |
| 8 | 1 | 1 | 0 | 25022 |
| 9 | 1 | 1 | 0 | 25116 |
| 10 | 1 | 1 | 0 | 25214 → 25307 on reopen, then 0 |
Count never grew 1, 2, 3… A new
camera.framethread appears per session and the previous one is gone. After the last close, count is 0. Pid stayed23191the whole time (no crash / process restart).Cycle 3 still had 1 thread at +7 s (state
R); the next idle window it dropped to 0 — delayed idle, not a leak. No logcat captured at that instant because the following cycle already showed 0.Photo smoke on the same process: first shutter did capture (
PHOTO_ORIENT capture_raw, 1856×1392), several captures in a row, no ANR / FATAL / process death. PSS still ratchets (~835 MB → ~1.07 GB over extra sessions; ~1.09 → 1.23 GB across the capture burst) — that is a separate native-memory issue, not this thread leak.We do not have the <4 GB Samsung in hand for this run; we will repeat
ps -Tthere when we do.guuhgalvao commentedon Oct 30, 2026
Follow-up on a 3 GB emulator cold boot (
Pixel_API_36,hw.ramSize=3G, MemTotal 3047 MB), same patched 5.1.1 build:ps -T | grep camera.frameover 10 open/close cycles in pid4580: 1 while open, 1 just after close, 0 at ~7 s. Thread ids changed each session; count never stacked.PSS still ratcheted: 761 MB (idle) → 922 MB (camera open) → 1304 MB after 10 sessions; MemAvailable 995 → 648 MB.
Photo smoke: first two shutters captured (
capture_raw). The process then disappeared (pid gone, JS dropped). The emulator itself stayed up; MemAvailable recovered to ~1.69 GB. So the thread leak looks fixed even under 3 GB; the remaining kill is the native PSS ratchet, not leftovercamera.framethreads.chocky335 commentedon Nov 3, 2026
Thanks @guuhgalvao, this is exactly the check I needed: one new
camera.framethread per session and none left after close, also on 3 GB. The PSS growth you see is outside this fix. #4227 only changes the thread lifetime, and a process kill with nocamera.framethreads left points at native memory that is not freed. A separate issue with a heap profile (adb shell dumpsys meminfo <pid>across sessions, or a Perfetto native heap trace) would give @mrousavy something to work with.