Skip to content

[Android] Repeated seek (recreate source node, same AudioBuffer) leaks native heap - never released by disconnect(), crashes via Hermes GC OOM #1263

Description

@WentTheFox

Description

Repeatedly creating, starting, stopping, and disconnecting AudioBufferSourceNode/GainNode pairs that all wrap the same, already-built AudioBuffer (the pattern required to reposition a one-shot source node - there's no seek() on a live AudioBufferSourceNode, in this library or any browser) leaks native heap. disconnect() does not release it.

A minimal repro app running 60 such "seeks" (~150ms apart, roughly human tapping speed, not synthetic max-speed spam) took resident memory from ~369 MB to 4637 MB - about 71 MB leaked per call, suspiciously close to the full buffer's own size (240s × 44100Hz × 2ch × 4 bytes ≈ 84.7 MB), which suggests each new source triggers an internal full copy of the PCM data rather than sharing/referencing the existing buffer. dumpsys meminfo confirms the leaked memory sits in Native Heap (Heap Size/Alloc reached ~5.2 GB, ~3.2 GB of it swapped to disk under the resulting pressure) - not Java/Dalvik heap, not graphics. Left running, this eventually crashes the app via a Hermes GC OOM (SIGSEGV in the mqt_v_js thread) once system memory pressure leaves nothing for Hermes to grow into.

I don't believe this is the same bug as #717 (clearOnEndedCallback, fixed in 0.10.0, which we're past) - the crash backtrace here has no react-native-audio-api frames at all (entirely inside libhermesvm.so/libreactnative.so's task scheduler, consistent with heap exhaustion rather than a direct native crash), and the leak itself is independently reproducible via dumpsys//proc/<pid>/status well before any crash occurs.

Originally found in a real app crashing under ordinary repeated seeking on a single track - the linked repro strips that down to a single-file app with no other dependencies.

Steps to reproduce

  1. Clone/run the linked repro
  2. Wait for "Ready." and note the starting RSS shown at the top
  3. Tap "Run 60 rapid seeks"
  4. Watch RSS climb continuously during the run and never drop back down afterward
  5. Repeat the tap a few more times / leave it running longer and the app eventually crashes with a Hermes GC OOM → SIGSEGV in mqt_v_js

Snack or a link to a repository

https://github.com/WentTheFox/AudioApiLeakRepro

React Native Audio API version

0.13.3

React Native version

0.87.0

Platforms

Android

JavaScript runtime

Hermes

Workflow

React Native (bare CLI)

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

Real device

Device model

Samsung Galaxy S24+ (SM-S926B), Android 16 (API 36), arm64-v8a

Acknowledgements

Yes

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    androidNative Android implementation, C++/Java/Kotlin bindings, or Android-specific issuesbugSomething isn't working as expected or produces unexpected errorspkg:audio-apiCore React Native audio control system compliant with the W3C Web Audio API spec

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions