From c001e191a19db32199ad75ccea219f729f9db7c4 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 7 Oct 2026 22:48:56 -0700 Subject: [PATCH] fix(mobile): collect UI runtime garbage on iOS memory warnings (#16296) Co-authored-by: Claude Opus 5.5 (1M context) --- apps/mobile/src/App.tsx | 3 +++ .../src/lib/useUiRuntimeMemoryWarningGc.ts | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 apps/mobile/src/lib/useUiRuntimeMemoryWarningGc.ts diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index f2a809361ee4322d21d846d245902108aee7ca38..86166278108b1412d7402a860e10dd606f66bf76 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -23,6 +23,7 @@ import { appAtomRegistry } from "./state/atom-registry"; import { OverlayPortalHost } from "./components/OverlayPortal"; import { shouldHandleAppLink } from "./lib/appLinking"; import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme"; +import { useUiRuntimeMemoryWarningGc } from "./lib/useUiRuntimeMemoryWarningGc"; import { SubscriptionUsageCoordinator } from "./widgets/SubscriptionUsageCoordinator"; import { VoiceInputProvider } from "./features/voice-input/VoiceInputProvider"; import { GlobalVoiceInputControl } from "./features/voice-input/GlobalVoiceInputControl"; @@ -57,6 +58,8 @@ function SplashScreenCoordinator() { } export default function App() { + useUiRuntimeMemoryWarningGc(); + return ( diff --git a/apps/mobile/src/lib/useUiRuntimeMemoryWarningGc.ts b/apps/mobile/src/lib/useUiRuntimeMemoryWarningGc.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e702f25aa17fce73e3fa899ee13a92faa272bfc --- /dev/null +++ b/apps/mobile/src/lib/useUiRuntimeMemoryWarningGc.ts @@ -0,0 +1,24 @@ +import { useEffect } from "react"; +import { AppState } from "react-native"; +import { scheduleOnUI } from "react-native-worklets"; + +function collectUiRuntimeGarbage() { + "worklet"; + // Hermes exposes `gc` on the worklets UI runtime; skip quietly on runtimes that do not. + const gc = (globalThis as { gc?: () => void }).gc; + if (typeof gc === "function") gc(); +} + +/** + * React Native only collects the main JS runtime on an iOS memory warning. The + * worklets UI runtime is a separate Hermes heap that holds native shadow nodes and + * serializables alive until its own GC runs, so collect it on the same warning. + */ +export function useUiRuntimeMemoryWarningGc() { + useEffect(() => { + const subscription = AppState.addEventListener("memoryWarning", () => { + scheduleOnUI(collectUiRuntimeGarbage); + }); + return () => subscription.remove(); + }, []); +} -- 2.54.0