react-native-reanimated icon indicating copy to clipboard operation
react-native-reanimated copied to clipboard

Massive memory leak with array values

Open eric-edouard opened this issue 3 years ago β€’ 8 comments

Description

We use reanimated for graph charts, which requires us to use array values in reanimated shared values. When doing this memory is allocated but when the component un-mounts, this memory is not cleared.

Expected behavior

The memory is cleared when component un-mounts and shared values are not used anymore

Actual behavior & steps to reproduce

  • Use a profiler to monitor memory usage of the app, in release mode
  • Create a component that uses arrays in reanimated shared values
  • un-mount this component
  • watch the memory not being cleared
  • mount & unmount this component until the app gets killed by the OS

Snack or minimal code example

Full minimal reproducible code here: https://github.com/Anybowdy/reanimated-leaks-example

Just need un-mount & remount a component like this multiple times:

export const AnimatedScreen: FC<Props> = ({ onPress }) => {
  const { goBack } = useNavigation();

  const largeArray = Array.from(Array(100000).keys());

  useSharedValue(largeArray);
  useSharedValue(largeArray);
  useSharedValue(largeArray);

  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <StyledButton text="Navigate back" onPress={() => goBack()} />
    </View>
  );
};

Package versions

name version
react-native 0.68.2
react-native-reanimated 2.8.0

Affected platforms

  • [ ] Android (not tested on android)
  • [x] iOS
  • [ ] Web

Video

eric-edouard avatar Jun 22 '22 14:06 eric-edouard

We are experiencing the exact same issue, would be nice if this was fixed. Could it be related to #2824 ?

Arjan-Zuidema avatar Jul 01 '22 09:07 Arjan-Zuidema

same issue here, I'm sure the memory leak occured in reaniamated's native thread, is there any plan to fix that?

richardo2016x avatar Jul 05 '22 17:07 richardo2016x

Something that can be a viable fix in some situations in the meantime is to move the shared values lower in the component tree. In the array of values solution it might look like this:

function Component({arrayOfItems}) {
    const sharedValueArray = useSharedValue(arrayOfItems.map(e=>0));
    return (
        <View>
            {arrayOfItems.map(e=>{
                return (
                    <Animated.View
                        {/* do something with shared value */}
                    />
                )
            }
        </View>
    )
}

Instead we can move the Animated.View into its own component like:

function ChildComponent({item}) {
    const animatedValue = useSharedValue(0);
    // ... rest of component
}

Then in the parent

function Component({arrayOfItems}) {
    return (
        <View>
            {arrayOfItems.map(e=>{
                return (
                    <ChildComponent/>
                )
            }
        </View>
    )
}

This can support a variable number of child components and avoid the memory leak since we're not creating any arrays. If you actually need to use the shared value in the parent you'd be kind of screwed though b/c you couldn't easily do the above without some really janky solution

iway1-hstk avatar Jul 21 '22 01:07 iway1-hstk

Thanks for the suggestion @iway1-hstk ! However in our case we don't have the choice, we need array in our shared values to compute coordinates on a chart, like here:

eric-edouard avatar Jul 25 '22 15:07 eric-edouard

Any updates on this? Seems like a pretty big problem.

jonahgervasi avatar Aug 24 '22 15:08 jonahgervasi

Hey! πŸ‘‹

The issue doesn't seem to contain a minimal reproduction.

Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

github-actions[bot] avatar Aug 24 '22 15:08 github-actions[bot]

Is this happening in older versions? I will test older ones to figure out when it broke (if it's not a long term issue)

Edit: confirmed across all versions to be an issue

marksyzm avatar Sep 03 '22 18:09 marksyzm

I have found a hacky fix, albeit not great, but it works. Strings have no problems with memory leaks, so I can confirm that if you stringify the data and then parse it when in use, it works perfectly for me for well over 5000 values so far. I'm very sure that will work great for the line graphs mentioned above.

notes.value = JSON.stringify(longArray);

useDerivedValue(() => {
  const notesValue = JSON.parse(note.value) as Notes[];
  // do something with it, it doesn't leak
});

marksyzm avatar Sep 03 '22 21:09 marksyzm

Hey @eric-edouard @marksyzm @Arjan-Zuidema @richardo2016x!

Just wanted to let you know that the whole core of shared values in Reanimated was basically rewritten in #3722 and released in 3.0.0-rc.8. Can you please check if this solves your issues?

tomekzaw avatar Dec 20 '22 15:12 tomekzaw

Thanks @tomekzaw - I'm planning to run it through my app over Christmas to test it out

marksyzm avatar Dec 20 '22 16:12 marksyzm

Hey @eric-edouard @marksyzm @Arjan-Zuidema @richardo2016x!

Just wanted to let you know that the whole core of shared values in Reanimated was basically rewritten in #3722 and released in 3.0.0-rc.8. Can you please check if this solves your issues?

Thanks @tomekzaw , it's nice of you to notify us! I will try the new rc release after my work this period and let you know if it works.

richardo2016x avatar Jan 03 '23 06:01 richardo2016x

Hey @richardo2016x, did you test how rewritten shared values works with array?

Rag0n avatar Feb 04 '23 01:02 Rag0n

@Rag0n I can confirm that this issue is fixed at 3.0.0-rc10.

lhreska avatar Feb 09 '23 19:02 lhreska

Hey @richardo2016x, did you test how rewritten shared values works with array?

Sorry for replying late, I am so busy the past time. We haven't upgraded react-native-reanimated to 3.0.0 due to other limitations of our biz. But I'm glad to see @lhreska mentioned they have confirmed it has been fixed in their application. πŸ˜€

richardo2016x avatar Feb 10 '23 16:02 richardo2016x

Any updates on this issue ?? This has been on the radar for over three years and, sadly, there is no definitive solution yet. The user feedback about these crashes is growing, and it's become a real pain point.

is there a solution for this or a reliable workaround ? Thanks for your attention to this matter.

  "react-native-screens": "^3.22.0",
  "@react-navigation/bottom-tabs": "^6.3.2",
  "@react-navigation/drawer": "^6.4.4",
  "@react-navigation/native": "^6.0.11",
  "@react-navigation/native-stack": "^6.7.0",
  "react-native-reanimated": "^3.3.0",
  "react-native-gesture-handler": "^2.12.0",

Solutions attempted so far without success:

- enableScreens(false)
- super.onCreate(null);

Logcat

┬───
β”‚ GC Root: Thread object
β”‚
β”œβ”€ android.net.ConnectivityThread instance
β”‚    Leaking: NO (PathClassLoader↓ is not leaking)
β”‚    Thread name: 'ConnectivityThread'
β”‚    ↓ Thread.contextClassLoader
β”œβ”€ dalvik.system.PathClassLoader instance
β”‚    Leaking: NO (InternalLeakCanary↓ is not leaking and A ClassLoader is never
β”‚    leaking)
β”‚    ↓ ClassLoader.runtimeInternalObjects
β”œβ”€ java.lang.Object[] array
β”‚    Leaking: NO (InternalLeakCanary↓ is not leaking)
β”‚    ↓ Object[2191]
β”œβ”€ leakcanary.internal.InternalLeakCanary class
β”‚    Leaking: NO (MainActivity↓ is not leaking and a class is never leaking)
β”‚    ↓ static InternalLeakCanary.resumedActivity
β”œβ”€ com.appname.MainActivity instance
β”‚    Leaking: NO (Activity#mDestroyed is false)
β”‚    mApplication instance of com.appname.MainApplication
β”‚    mBase instance of androidx.appcompat.view.ContextThemeWrapper
β”‚    ↓ AppCompatActivity.mDelegate
β”‚                        ~~~
β”œβ”€ androidx.appcompat.app.AppCompatDelegateImpl instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 1.0 kB in 16 objects
β”‚    mAppCompatCallback instance of com.appname.MainActivity with
β”‚    mDestroyed = false
β”‚    mContext instance of com.appname.MainActivity with mDestroyed = false
β”‚    mHost instance of com.appname.MainActivity with mDestroyed = false
β”‚    ↓ AppCompatDelegateImpl.mActionBar
β”‚                            ~~~~
β”œβ”€ androidx.appcompat.app.ToolbarActionBar instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 552.4 kB in 2395 objects
β”‚    ↓ ToolbarActionBar.mDecorToolbar
β”‚                       ~~~~~
β”œβ”€ androidx.appcompat.widget.ToolbarWidgetWrapper instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 552.4 kB in 2391 objects
β”‚    ↓ ToolbarWidgetWrapper.mToolbar
β”‚                           ~~~~
β”œβ”€ com.swmansion.rnscreens.ScreenStackHeaderConfig$DebugMenuToolbar instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 552.2 kB in 2387 objects
β”‚    View not part of a window view hierarchy
β”‚    View.mAttachInfo is null (view detached)
β”‚    View.mWindowAttachCount = 1
β”‚    mPopupContext instance of com.facebook.react.uimanager.ThemedReactContext,
β”‚    wrapping activity com.appname.MainActivity with mDestroyed = false
β”‚    mContext instance of com.facebook.react.uimanager.ThemedReactContext,
β”‚    wrapping activity com.appname.MainActivity with mDestroyed = false
β”‚    ↓ CustomToolbar.config
β”‚                    ~~
β”œβ”€ com.swmansion.rnscreens.ScreenStackHeaderConfig instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 2.0 kB in 14 objects
β”‚    View not part of a window view hierarchy
β”‚    View.mAttachInfo is null (view detached)
β”‚    View.mID = R.id.null
β”‚    View.mWindowAttachCount = 1
β”‚    mContext instance of com.facebook.react.uimanager.ThemedReactContext,
β”‚    wrapping activity com.appname.MainActivity with mDestroyed = false
β”‚    ↓ View.mParent
β”‚           ~~~
β”œβ”€ com.swmansion.rnscreens.Screen instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 511.8 kB in 1829 objects
β”‚    View not part of a window view hierarchy
β”‚    View.mAttachInfo is null (view detached)
β”‚    View.mID = R.id.null
β”‚    View.mWindowAttachCount = 1
β”‚    mContext instance of com.facebook.react.uimanager.ThemedReactContext,
β”‚    wrapping activity com.appname.MainActivity with mDestroyed = false
β”‚    ↓ Screen.fragment
β”‚             ~~~~
β•°β†’ com.swmansion.rnscreens.ScreenStackFragment instance
​     Leaking: YES (ObjectWatcher was watching this because com.swmansion.
​     rnscreens.ScreenStackFragment received Fragment#onDestroy() callback and
​     Fragment#mFragmentManager is null)
​     Retaining 2.1 kB in 72 objects
​     key = b116c7d1-e55c-4a42-9170-eca82ba9dd7d
​     watchDurationMillis = 7292
​     retainedDurationMillis = 2249

METADATA

Build.VERSION.SDK_INT: 28
Build.MANUFACTURER: HUAWEI
LeakCanary version: 2.11
App process name: appname
Class

Logcat 2

───
β”‚ GC Root: Global variable in native code
β”‚
β”œβ”€ com.swmansion.reanimated.NativeProxy instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 221 B in 8 objects
β”‚    ↓ NativeProxyCommon.mNodesManager
β”‚                        ~~~~~
β”œβ”€ com.swmansion.reanimated.NodesManager instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 9.4 kB in 318 objects
β”‚    mContext instance of com.facebook.react.bridge.ReactApplicationContext,
β”‚    wrapping com.appname.MainApplication
β”‚    mReactApplicationContext instance of com.facebook.react.bridge.
β”‚    ReactApplicationContext, wrapping com.appname.MainApplication
β”‚    ↓ NodesManager.mAnimationManager
β”‚                   ~~~~~~~
β”œβ”€ com.swmansion.reanimated.layoutReanimation.AnimationsManager instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 794 B in 24 objects
β”‚    mContext instance of com.facebook.react.bridge.ReactApplicationContext,
β”‚    wrapping com.appname.MainApplication
β”‚    ↓ AnimationsManager.mReanimatedNativeHierarchyManager
β”‚                        ~~~~~~~~~~~
β”œβ”€ com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager
β”‚  instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 942.6 kB in 6901 objects
β”‚    ↓ NativeViewHierarchyManager.mTagsToViews
β”‚                                 ~~~~
β”œβ”€ android.util.SparseArray instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 929.7 kB in 6881 objects
β”‚    ↓ SparseArray.mValues
β”‚                  ~~~
β”œβ”€ java.lang.Object[] array
β”‚    Leaking: UNKNOWN
β”‚    Retaining 923.6 kB in 6879 objects
β”‚    ↓ Object[217]
β”‚            ~~~
β”œβ”€ com.swmansion.rnscreens.Screen instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 2.2 kB in 17 objects
β”‚    View not part of a window view hierarchy
β”‚    View.mAttachInfo is null (view detached)
β”‚    View.mID = R.id.null
β”‚    View.mWindowAttachCount = 5
β”‚    mContext instance of com.facebook.react.uimanager.ThemedReactContext,
β”‚    wrapping activity com.appname.MainActivity with mDestroyed = false
β”‚    ↓ View.mParent
β”‚           ~~~
β•°β†’ com.swmansion.rnscreens.ScreenStackFragment$ScreensCoordinatorLayout instance
​     Leaking: YES (ObjectWatcher was watching this because com.swmansion.
​     rnscreens.ScreenStackFragment received Fragment#onDestroyView() callback
​     (references to its views should be cleared to prevent leaks))
​     Retaining 3.7 kB in 72 objects
​     key = 8b340022-4f6e-4653-a6c8-ad5639e3ff8e
​     watchDurationMillis = 5828
​     retainedDurationMillis = 827
​     View not part of a window view hierarchy
​     View.mAttachInfo is null (view detached)
​     View.mWindowAttachCount = 1
​     mContext instance of com.appname.MainActivity with mDestroyed = false

METADATA

Build.VERSION.SDK_INT: 28
Build.MANUFACTURER: HUAWEI
LeakCanary version: 2.11
App process name:appname
Class count: 18907
Instance count: 266551
Primitive array count: 172560
Object array count: 29962
Thread count: 57
Heap total bytes: 26966868
Bitmap count: 75
Bitmap total bytes: 14020308
Large bitmap count: 0
Large bitmap total bytes: 0
Db 1: open /data/user/0/appnamer/databases/RKStorage
Db 2: open /data/user/0/com.appname/databases/com.
google.android.datatransport.events
Db 3: open /data/user/0/com.appname/no_backup/androidx.work.workdb
Stats: LruCache[maxSize=3000,hits=84650,misses=180148,hitRate=31%]
RandomAccess[bytes=9194085,reads=180148,travel=66525003196,range=33782477,size=4
0598308]
Analysis duration: 15559 ms

Ahmed-Imam avatar Jun 29 '23 14:06 Ahmed-Imam

A fresh repo with memory leak on Android https://github.com/Ahmed-Imam/memoryLeak

Ahmed-Imam avatar Jul 02 '23 19:07 Ahmed-Imam

This is still an issue both on iOS and Android

amitach avatar Aug 08 '23 08:08 amitach

Hey! πŸ‘‹

The issue doesn't seem to contain a minimal reproduction.

Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

github-actions[bot] avatar Aug 08 '23 08:08 github-actions[bot]

Problem is resolved since version 3+

piaskowyk avatar Aug 08 '23 15:08 piaskowyk

Yes verified, it is fixed, THanks

amitach avatar Aug 08 '23 17:08 amitach

Do we have any patch for lower versions @piaskowyk

aayushanand99 avatar Aug 17 '23 14:08 aayushanand99