question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItΒ collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Android] Memory leak leading to intermittent crashing

See original GitHub issue

It looks like a memory leak in MainActivity related to the ScreenFragment onDestroy function is causing app crashes.

We have a Tab Navigator with a nested Auth Stack Navigator for login flows. We are using react-native-screens with enableScreens() at the top of App.tsx outside of the root component.

When we turn off enableScreens() the memory leak goes away. But this causes exceptionally poor performance on Android 8 and lower-end devices.

{
    "react-native-screens": "^2.11.0",
    "react-native": "0.63.3",
    "@react-navigation/native": "^5.7.6",
    "@react-navigation/stack": "^5.9.3",
}

LeakCanary report:

┬───
β”‚ GC Root: Global variable in native code
β”‚
β”œβ”€ com.facebook.react.bridge.JavaModuleWrapper instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ JavaModuleWrapper.mModuleHolder
β”‚                        ~~~~~~~~~~~~~
β”œβ”€ com.facebook.react.bridge.ModuleHolder instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ ModuleHolder.mModule
β”‚                   ~~~~~~~
β”œβ”€ com.facebook.react.uimanager.UIManagerModule instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ UIManagerModule.mUIImplementation
β”‚                      ~~~~~~~~~~~~~~~~~
β”œβ”€ com.facebook.react.uimanager.UIImplementation instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ UIImplementation.mOperationsQueue
β”‚                       ~~~~~~~~~~~~~~~~
β”œβ”€ com.facebook.react.uimanager.UIViewOperationQueue instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ UIViewOperationQueue.mNativeViewHierarchyManager
β”‚                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~
β”œβ”€ com.facebook.react.uimanager.NativeViewHierarchyManager instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ NativeViewHierarchyManager.mTagsToViews
β”‚                                 ~~~~~~~~~~~~
β”œβ”€ android.util.SparseArray instance
β”‚    Leaking: UNKNOWN
β”‚    ↓ SparseArray.mValues
β”‚                  ~~~~~~~
β”œβ”€ java.lang.Object[] array
β”‚    Leaking: UNKNOWN
β”‚    ↓ Object[].[65]
β”‚               ~~~~
β”œβ”€ com.swmansion.rnscreens.Screen instance
β”‚    Leaking: YES (View detached and has parent)
β”‚    mContext instance of com.facebook.react.uimanager.ThemedReactContext, wrapping activity com.XXXX.MainActivity with mDestroyed = false
β”‚    View#mParent is set
β”‚    View#mAttachInfo is null (view detached)
β”‚    View.mID = R.id.null
β”‚    View.mWindowAttachCount = 1
β”‚    ↓ Screen.mFragment
β•°β†’ com.swmansion.rnscreens.ScreenFragment instance
​     Leaking: YES (ObjectWatcher was watching this because com.swmansion.rnscreens.ScreenFragment received Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
​     key = 55523ed9-b81c-4d11-a5b7-42155878b7f6
​     watchDurationMillis = 7610
​     retainedDurationMillis = 2572
METADATA
Build.VERSION.SDK_INT: 29
Build.MANUFACTURER: OnePlus
LeakCanary version: 2.4
App process name: com.XXXX
Analysis duration: 12898 ms

MainTabNavigator.tsx

const Tab = createBottomTabNavigator<MainNavParamList>()

const MainTabNavigator: FC<{}> = () => {
  return (
    <Tab.Navigator initialRouteName={'Home'} tabBarOptions={tabBarOptions}>
      <Tab.Screen
        name="Home"
        component={HomeStack}
        options={{ tabBarLabel: tabBarLabel(I18n.t('Home')), tabBarIcon: tabBarIcon('home-alt') }}
      />
      <Tab.Screen
        name="Menu"
        component={MenuStack}
        options={{ tabBarLabel: tabBarLabel(I18n.t('Menu')), tabBarIcon: tabBarIcon('coffee') }}
      />
      <Tab.Screen
        name="Order"
        component={OrderStack}
        options={{ tabBarLabel: tabBarLabel(I18n.t('Orders')), tabBarIcon: tabBarIcon('receipt') }}
      />
      <Tab.Screen
        name="Account"
        component={AccountStack}
        options={{ tabBarLabel: tabBarLabel(I18n.t('Account')), tabBarIcon: tabBarIcon('user') }}
      />
      <Tab.Screen name="Cart" component={CartStackNavigator} options={{ tabBarButton: () => null, tabBarVisible: false }} />
      <Tab.Screen name="Auth" component={LoginStack} options={{ tabBarButton: () => null }} />
    </Tab.Navigator>
  )
}

Auth Stack Navigator

const Stack = createStackNavigator<AuthStackParamList>()

interface Props {
  navigation?: StackNavigationProp<MainNavParamList, 'Auth'>
  route?: RouteProp<MainNavParamList, 'Auth'>
}

export type LeftAction = ((props: StackHeaderLeftButtonProps) => React.ReactNode) | undefined

const AuthFlowNavigator: React.FC<Props> = (props: Props) => {
  let initialRoute: AuthInitialScreen = 'SignIn'
  if (props.route && props.route.params) {
    initialRoute = props.route.params.screen || initialRoute
  }

  let goBack: LeftAction
  const navigation = props.navigation
  if (navigation) {
    goBack = (stackProps: StackHeaderLeftButtonProps) => (
      <HeaderBackButton
        {...stackProps}
        onPress={() => navigation.goBack()}
        backImage={(imgProps: { tintColor: string }) => <BackIcon tintColor={imgProps.tintColor} />}
      />
    )
  }

  return (
    <Stack.Navigator
      mode="card"
      headerMode="float"
      initialRouteName={initialRoute}
      screenOptions={stackNavigationOptions}>
      <Stack.Screen
        name="SignIn"
        component={SignIn}
        options={{
          headerTitle: I18n.t('Sign in to my Account'),
          headerLeft: goBack,
        }}
      />
      <Stack.Screen
        name="SignUp"
        component={SignUp}
        options={{
          headerTitle: I18n.t('Sign up'),
          headerLeft: initialRoute === 'SignUp' ? goBack : undefined,
        }}
      />
      <Stack.Screen
        name="SignUpWithEmail"
        component={SignUpWithEmail}
        options={{
          headerTitle: I18n.t('Sign up by email'),
        }}
      />
      <Stack.Screen
        name="ConfirmSignUp"
        component={ConfirmSignUp}
        options={{
          headerTitle: I18n.t('Sign up by email'),
        }}
      />
      <Stack.Screen
        name="ForgotPasswordEmail"
        component={ForgotPasswordEmail}
        options={{ headerTitle: I18n.t('Reset password') }}
      />
      <Stack.Screen
        name="ForgotPasswordChallenge"
        component={ForgotPasswordChallenge}
        options={{ headerTitle: I18n.t('Reset password') }}
      />
    </Stack.Navigator>
  )
}

MainActivity.java

import android.os.Bundle;

import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;

import org.devio.rn.splashscreen.SplashScreen;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript. This is used to schedule
     * rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "XXXX";
    }

    @Override
    protected ReactActivityDelegate createReactActivityDelegate() {

        return new ReactActivityDelegate(this, getMainComponentName()) {
            @Override
            protected ReactRootView createRootView() {
                return new RNGestureHandlerEnabledRootView(MainActivity.this);
            }
        };
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        SplashScreen.show(this, R.style.SplashStatusBarTheme);
        super.onCreate(null);
    }
}

The memory dump occurs when you navigate to the Auth Stack. App doesn’t seem to crash, however.

Crashes are reported when user is redirected to Auth Stack when trying to place an order while not logged in. These crashes are intermittent and cannot be reliably induced.

Cheers, Jon

EDIT:

App Center Diagnostics shows this stack trace occuring multiple times, for only Android users:

com.swmansion.rnscreens.ScreenFragment.<init> ScreenFragment.java:44
java.lang.reflect.Constructor.newInstance0 Constructor.java
androidx.fragment.app.Fragment.instantiate Fragment.java:548
androidx.fragment.app.FragmentContainer.instantiate FragmentContainer.java:57
androidx.fragment.app.FragmentManager$3.instantiate FragmentManager.java:390
androidx.fragment.app.FragmentStateManager.<init> FragmentStateManager.java:74
androidx.fragment.app.FragmentManager.restoreSaveState FragmentManager.java:2454
androidx.fragment.app.FragmentController.restoreSaveState FragmentController.java:196
androidx.fragment.app.FragmentActivity.onCreate FragmentActivity.java:287
androidx.appcompat.app.AppCompatActivity.onCreate AppCompatActivity.java:106
com.facebook.react.ReactActivity.onCreate ReactActivity.java:44
com.XXXX.MainActivity.onCreate MainActivity.java:37
android.app.Activity.performCreate Activity.java:8000

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:18 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
Ahmed-Imamcommented, Apr 22, 2021

Any updates on this, I am still facing the same issue

"react-native": "0.63.4",
"react-navigation": "^4.0.0",   
"react-navigation-material-bottom-tabs": "^1.0.0",   
"react-navigation-stack": "^2.10.4",   
"react-navigation-tabs": "^2.3.0",

as @d3vhound said super.onCreate(null) didn’t fix the issue.

Screen Shot 2021-04-22 at 4 32 13 PM

1reaction
WoLewickicommented, Sep 22, 2021

Then I think there is no more to be done unfortunately, as mentioned here: https://github.com/software-mansion/react-native-screens/issues/843#issuecomment-832034119.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Everything you need to know about Memory Leaks in Android.
Memory leaks can lead to lags in the app, ANR errors, and OutOfMemory exceptions. Which ultimately leads to the uninstallation of your app...
Read more >
Preventing and detecting memory leaks in Android apps
Memory leaks can cause your Android app to crash, causing frustration and lower usage. Learn how to fix them in this guide.
Read more >
How to Find and Fix a Memory Leak in Android
If your Android phone keeps crashing, it can be due to a memory leak. Here are a few ways for you to find...
Read more >
Memory leaks in Android β€” identify, treat and avoid - Medium
A common cause of crashes are memory leaks. This particular problem can manifest itself in various forms. In most cases we see a...
Read more >
Android app out of memory issues - tried everything and still at ...
The biggest source of memory leak I have found was caused by some global, high level or long-standing reference ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found