Skip to content

Android removes and deletes every view of an unmounted subtree inside the frame that unmounts it #59448

Open

Description

@YevheniiKotyrlo

Description

When a subtree unmounts on Fabric Android, the frame that unmounts it runs one REMOVE and one DELETE mount instruction for every host view in the subtree. Only the root's removal changes what is on screen; its descendants leave the window with it, so their instructions cost the frame without changing a pixel.

The differ gives each removed child a DeleteMutation and a RemoveMutation and then diffs its subtree against no children (Differentiator.cpp, and the same in the reordering branch at L1264-L1284), so every descendant gets a pair of its own. SurfaceMountingManager executes each pair on the UI thread inside the batch: removeViewAt takes the view out of its parent ViewGroup (SurfaceMountingManager.kt), and deleteView destroys its state wrapper and event emitter and calls onDropViewInstance (SurfaceMountingManager.kt).

In the reproducer's release build on a Galaxy S23 (Android 14, 120 Hz, an 8.33 ms budget), unmounting a 241-view panel takes a 9.0–12.6 ms frame, of which REMOVE 241 is 4.8–6.8 ms and DELETE 241 1.8–2.6 ms.

RemoveDeleteTree (b6bbbf8, ca8481b) removed a subtree's root at once and deferred the rest of the teardown, behind a flag that was off in open source, and #45385 removed it as not worth its complexity. Here the per-view teardown is most of an over-budget frame. Would you take a change that removes the root inside the frame and runs its descendants' teardown after it, or is that the trade-off #45385 already decided?

Steps to reproduce

  1. Build the reproducer's app in release: cd ReproducerApp && yarn install, then cd android && ./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a, and install app/build/outputs/apk/release/app-release.apk. It mounts and unmounts a panel of 40 rows, 241 host views, once a second.
  2. Record a 10 s Perfetto trace with the repository's trace.cfg (the gfx and view atrace categories and the app's own sections), starting the app after the trace, as its README shows.
  3. On the app's main thread, every Choreographer#doFrame that unmounts the panel holds IntBufferBatchMountItem::mountInstructions::REMOVE numInstructions=241 and IntBufferBatchMountItem::mountInstructions::DELETE numInstructions=241.

React Native Version

0.88.0

Affected Platforms

Runtime - Android

Areas

Fabric - The New Renderer

Output of npx @react-native-community/cli info

System:
  OS: Windows 11 10.0.26300
  CPU: "(32) x64 AMD Ryzen 9 9950X3D 16-Core Processor          "
  Memory: 172.51 GB / 255.67 GB
Binaries:
  Node:
    version: 26.12.1
    path: C:\Program Files\nodejs\node.EXE
  Yarn:
    version: 1.22.22
    path: ~\AppData\Roaming\npm\yarn.CMD
  npm:
    version: 12.2.1
    path: C:\Program Files\nodejs\npm.CMD
  Watchman: Not Found
SDKs:
  Android SDK:
    API Levels:
      - "33"
      - "35"
      - "35"
      - "35"
      - "36"
      - "37"
    Build Tools:
      - 35.0.0
      - 36.0.0
      - 36.1.0
      - 37.0.0
    System Images:
      - android-26 | Google APIs Intel x86_64 Atom
      - android-28 | Google APIs Intel x86_64 Atom
      - android-29 | Google APIs Intel x86_64 Atom
      - android-30 | Google APIs Intel x86_64 Atom
      - android-31 | Google APIs Intel x86_64 Atom
      - android-32 | Google APIs Intel x86_64 Atom
      - android-33 | Google APIs Intel x86_64 Atom
      - android-34 | Google APIs Intel x86_64 Atom
      - android-35 | Google APIs Intel x86_64 Atom
      - android-36.1 | Google Play Intel x86_64 Atom
      - android-36.1 | Pre-Release 16 KB Page Size Google Play Intel x86_64 Atom
      - android-37.0 | Google APIs Intel x86_64 Atom
    Android NDK: Not Found
  Windows SDK: Not Found
IDEs:
  Android Studio: AI-262.9437.185.2621.16467767
  Visual Studio: Not Found
Languages:
  Java:
    version: 17.0.20.1
    path: C:\Program Files\Microsoft\jdk-17.0.20.101-hotspot\bin\javac.EXE
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli":
    installed: 20.2.0
    wanted: 20.2.0
  react:
    installed: 19.3.0
    wanted: 19.3.0
  react-native:
    installed: 0.88.0
    wanted: 0.88.0
  react-native-windows: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Run in the reproducer's ReproducerApp.

Stacktrace or Logs

No crash. Galaxy S23, Android 14, 120 Hz; the reproducer's release build on 0.88.0,
the unmount frames of one 10 s trace, each with its mount-instruction batches:

+2118.6ms  doFrame  9.04ms  REMOVE 241 in 4.76ms · UPDATE_LAYOUT 2 in 0.06ms · UPDATE_OVERFLOW_INSET 2 in 0.05ms · DELETE 241 in 1.87ms
+4152.0ms  doFrame 12.62ms  REMOVE 241 in 6.82ms · UPDATE_LAYOUT 2 in 0.04ms · UPDATE_OVERFLOW_INSET 2 in 0.06ms · DELETE 241 in 2.40ms
+6179.8ms  doFrame 11.29ms  REMOVE 241 in 6.25ms · UPDATE_LAYOUT 2 in 0.05ms · UPDATE_OVERFLOW_INSET 2 in 0.04ms · DELETE 241 in 2.62ms
+8230.4ms  doFrame  9.90ms  REMOVE 241 in 5.54ms · UPDATE_LAYOUT 2 in 0.03ms · UPDATE_OVERFLOW_INSET 2 in 0.04ms · DELETE 241 in 1.81ms

The mount frames between them, with CREATE 241 in 54–59 ms, are what #59248 describes.

MANDATORY Reproducer

https://github.com/YevheniiKotyrlo/rn-fabric-subtree-unmount

Screenshots and Videos

No response

Activity

  1. dave-otf commented on Oct 25, 2026

    @dave-otf

    My take: this warrants reopening a narrow experiment, but not assuming the old implementation should be restored unchanged. The reported unmount frames are 9.0–12.6 ms against an 8.33 ms budget; REMOVE alone is 4.8–6.8 ms and DELETE 1.8–2.6 ms for 241 views. Since REMOVE dominates, deferring only DELETE would miss most of this case. I would compare root detach plus deferred descendant REMOVE/DELETE against current batching, including LayoutAnimation, reattachment/key reuse, and the onDropViewInstance/event-emitter cleanup path. That would test whether the extra lifecycle complexity pays for this measured workload.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions