Skip to content

OutOfMemoryError when displaying paywall with social proof carousel #1492

@rgomezp

Description

@rgomezp

Describe the bug

The RevenueCat native Compose paywall UI (react-native-purchases-ui) causes consistent OutOfMemoryError crashes on Android when displaying paywall with social proof carousel. The paywall exhausts the 256MB heap limit during its initial render, even with a simplified template containing no custom images or complex UI elements.

1. Environment

  1. Platform: Android
  2. SDK version:
    • react-native-purchases: 9.6.6
    • react-native-purchases-ui: 9.6.6
  3. OS version: Android 16
  4. Android Studio version: N/A (using Expo managed workflow)
  5. React Native version: 0.79.6
  6. SDK installation: npm/yarn with Expo managed workflow
  7. How widespread is the issue: 100% reproducible on test devices when displaying hard paywall via RevenueCatUI.Paywall

2. Debug logs

The crash occurs during the initial Compose UI measurement/layout phase:

2025-11-18 14:17:38.341 21374-21374 com.example.app         com.example.app              W  Throwing OutOfMemoryError "Failed to allocate a 4224 byte allocation with 2036688 free bytes and 1988KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC."

2025-11-18 14:17:38.347 21374-21374 AndroidRuntime          com.example.app              E  FATAL EXCEPTION: main
Process: com.example.app, PID: 21374
java.lang.OutOfMemoryError: Failed to allocate a 4224 byte allocation with 2036688 free bytes and 1988KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC.
	at android.os.Parcel.nativeReadString8(Native Method)
	at android.os.Parcel.readString8NoHelper(Parcel.java:3436)
	at android.os.Parcel$ReadWriteHelper.readString8(Parcel.java:545)
	at android.os.Parcel.readString8(Parcel.java:3415)
	at android.view.DisplayShape$1.createFromParcel(DisplayShape.java:261)
	at android.view.InsetsState.readFromParcel(InsetsState.java:852)
	at android.window.WindowMetricsController.getWindowInsetsFromServerForDisplay(WindowMetricsController.java:117)
	at androidx.compose.material3.adaptive.AndroidWindowAdaptiveInfo_androidKt.currentWindowSize(AndroidWindowAdaptiveInfo.android.kt:58)
	at androidx.compose.material3.adaptive.AndroidWindowAdaptiveInfo_androidKt.currentWindowAdaptiveInfo(AndroidWindowAdaptiveInfo.android.kt:37)
	at com.revenuecat.purchases.ui.revenuecatui.components.iconcomponent.IconComponentStateKt.rememberUpdatedIconComponentState(IconComponentState.kt:50)
	at com.revenuecat.purchases.ui.revenuecatui.components.iconcomponent.IconComponentViewKt.IconComponentView(IconComponentView.kt:47)
	at com.revenuecat.purchases.ui.revenuecatui.components.ComponentViewKt.ComponentView(ComponentView.kt:81)
	at com.revenuecat.purchases.ui.revenuecatui.components.stack.StackComponentViewKt$MainStackComponent$stack$1$2$1$1.invoke(StackComponentView.kt:513)
	[... extensive Compose layout stack trace ...]
	at com.revenuecat.purchases.react.ui.views.ComposeViewWrapper.onMeasure(ComposeViewWrapper.kt:52)

Key observations from logs:

  • Memory pressure builds up during onboarding: Clamp target GC heap from 351MB to 256MB
  • Multiple blocking GC events before crash: WaitForGcToComplete blocked Alloc (400-600ms delays)
  • Heap is at 254-255MB before paywall renders
  • Crash occurs during Compose UI measurement, specifically when calculating adaptive window metrics

3. Steps to reproduce

Setup:

  1. Create a React Native app with Expo managed workflow
  2. Add RevenueCat paywall using react-native-purchases-ui at the end of onboarding
  3. Use the default offering

Expected behavior:
Paywall should render successfully without memory issues.

Actual behavior:
App crashes with OutOfMemoryError during paywall's initial Compose measurement phase. The crash is 100% reproducible.

Additional observations:

  • Removing all WebP images from onboarding slides does NOT fix the issue
  • Removing all custom images from the paywall template does NOT fix the issue
  • Memory is already at 254-255MB before the paywall attempts to render
  • Adding android:largeHeap="true" does NOT fix the issue (still crashes at 256MB limit)

4. Other information

Root cause analysis:

The RevenueCat Compose paywall UI appears to have excessive memory overhead when rendering paywalls with social proof carousel. The stack trace shows it's crashing during:

  1. Adaptive window metrics calculation (currentWindowAdaptiveInfo)
  2. Icon component state management
  3. Complex nested Stack/Carousel component composition

The issue appears to be related to:

  1. Jetpack Compose memory overhead - The native Compose UI requires significantly more memory than React Native views
  2. Complex component hierarchy - Multiple nested stacks, carousels, and adaptive layouts
  3. No memory cleanup between screens - Previous onboarding state remains in memory

Workarounds attempted:

  • ✅ Removed all images from onboarding slides
  • ✅ Simplified paywall template (removed all custom images)
  • ✅ Added android:largeHeap="true"
  • ❌ None of these resolved the issue

Removing the carousel:

  • ✅ This seems to have fixed the crash

Device specifications:

  • Heap limit: 256MB (standard Android allocation)
  • Memory available at crash: ~2MB (254MB/256MB used)
  • React Native memory footprint before paywall: ~200MB

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions