Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pseudo memory leak after removing many Views from screen (ios) #21454

Closed
3 tasks done
leonardocarvalho opened this issue Oct 2, 2018 · 10 comments
Closed
3 tasks done
Labels
Bug Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. JavaScript Platform: iOS iOS applications. Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@leonardocarvalho
Copy link

leonardocarvalho commented Oct 2, 2018

Environment

  React Native Environment Info:
    System:
      OS: macOS High Sierra 10.13.6
      CPU: x64 Intel(R) Core(TM) i5-6360U CPU @ 2.00GHz
      Memory: 84.61 MB / 8.00 GB
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 9.11.2 - ~/.nvm/versions/node/v9.11.2/bin/node
      Yarn: 1.5.1 - /usr/local/bin/yarn
      npm: 5.6.0 - ~/.nvm/versions/node/v9.11.2/bin/npm
      Watchman: 4.7.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 5.0
      Android SDK:
        Build Tools: 23.0.1, 23.0.3, 24.0.0, 24.0.1, 25.0.0, 25.0.1, 25.0.2, 25.0.3, 26.0.1, 26.0.2, 27.0.0, 27.0.3
        API Levels: 23, 24, 25, 26, 27
    IDEs:
      Android Studio: 3.1 AI-173.4670197
      Xcode: 10.0/10A255 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.5.0 => 16.5.0 
      react-native: 0.57.1 => 0.57.1 
    npmGlobalPackages:
      create-react-native-app: 2.0.2

Description

Disclaimer: Yet not totally sure if the behavior is an issue, but as it's causing a somewhat large amount of crashes in my app I'm considering it to be a bug.

The issue is that after performing operations expected to have no impact in memory consumption the total memory usage is higher then the previous baseline. Trying to isolate the cause I ended up with a fairly simple app (shown below) that adds and removes views from the screen and have this pseudo memory leak.

XCode tools to track memory indicate that: (all tests made in production scheme)

1. Adding some views and then removing them memory consumption increases
screen shot 2018-09-29 at 10 25 17

2. Add and remove the same amount of views again doesn't increase the memory consumption, but if a larger number of views are added some more memory leak
screen shot 2018-09-29 at 10 56 05

Note that the second peak in the graph is lower than the first. Also, from time to time, some of the retained memory is freed (not in the graph), but I couldn't grasp the possible trigger. Considering this pattern I'm inclined to think this is some kind of cache and not a leak, but I couldn't find anything about it.

In my production app a similar pattern can be repeated until the device runs out of memory. This was tested in iPhone5, but there are similar reports on Crashlytics for other devices.

I was worried that this could be a simple to answer question, so I tried StackOverflow without success so far.

Reproducible Demo

The issue is reproducible by react-native init and changing the App.js to be

import React from 'react';                                                                                                                                                                             
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';                                                                                                                               

export default class App extends React.Component {                                                                                                                                                     
  constructor(props) {                                                                                                                                                                                 
    super(props);                                                                                                                                                                                      
    this.state = {                                                                                                                                                                                     
      count: 0,                                                                                                                                                                                        
    };                                                                                                                                                                                                 
  }                                                                                                                                                                                                    

  render() {                                                                                                                                                                                           
    const { count } = this.state;                                                                                                                                                                      
    const extraContent = new Array(200 * count).fill().map((_, index) => (                                                                                                                             
      <View key={index}><Text>Line {index}</Text></View>                                                                                                                                               
    ));                                                                                                                                                                                                

    return (                                                                                                                                                                                           
      <View style={styles.container}>                                                                                                                                                                  
        <View style={styles.actions}>                                                                                                                                                                  
          <TouchableOpacity onPress={() => this.setState({ count: count + 1})}>                                                                                                                        
            <View style={styles.button}>                                                                                                                                                               
              <Text style={styles.buttonText}>Add</Text>                                                                                                                                               
            </View>                                                                                                                                                                                    
          </TouchableOpacity>                                                                                                                                                                          
          <TouchableOpacity onPress={() => count > 0 && this.setState({ count: count - 1})}>                                                                                                           
            <View style={styles.button}>                                                                                                                                                         
              <Text style={styles.buttonText}>Remove</Text>                                                                                                                                            
            </View>                                                                                                                                                                                    
          </TouchableOpacity>                                                                                                                                                                          
        </View>                                                                                                                                                                                        
        <View>                                                                                                                                                                                         
          <Text>Current count: {count}</Text>                                                                                                                                                          
          <View>{extraContent}</View>                                                                                                                                                                  
        </View>                                                                                                                                                                                        
      </View>                                                                                                                                                                                          
    );                                                                                                                                                                                                 
  }                                                                                                                                                                                                    
}                                                                                                                                                                                                      

const styles = StyleSheet.create({                                                                                                                                                                     
  container: {                                                                                                                                                                                         
    flex: 1,                                                                                                                                                                                           
    marginTop: 50,                                                                                                                                                                                     
    width: '100%',                                                                                                                                                                                     
  },                                                                                                                                                                                                   
  actions: {                                                                                                                                                                                           
    flexDirection: 'row',                                                                                                                                                                              
    alignItems: 'center',                                                                                                                                                                              
    justifyContent: 'space-around',                                                                                                                                                                    
  },                                                                                                                                                                                                   
  buttonText: {                                                                                                                                                                                        
    color: '#ffffff',                                                                                                                                                                                  
  },                                                                                                                                                                                                   
  button: {                                                                                                                                                                                            
    alignItems: 'center',                                                                                                                                                                              
    justifyContent: 'center',                                                                                                                                                                          
    backgroundColor: '#95afe5',                                                                                                                                                                        
    height: 50,                                                                                                                                                                                        
    width: 100,                                                                                                                                                                                        
    marginBottom: 5,                                                                                                                                                                                   
    borderRadius: 5,                                                                                                                                                                                   
  },                                                                                                                                                                                                   
});
@kelset kelset added the Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. label Oct 5, 2018
@kelset
Copy link
Contributor

kelset commented Oct 5, 2018

Hey thanks for the report, it indeed seems to be a memory leak issue 🤔

Have you also tried to investigate it following the suggestions from this article?

Do you have a similar effect on Android too?

@leonardocarvalho
Copy link
Author

leonardocarvalho commented Oct 5, 2018

Hey @kelset, thanks for your response.

I've tried everything js related in the article, with no success. The example doesn't have many timers or closures to retain the objects.

I was particularly excited about the setState anywhere in the tree, but it had no effect in the repro.

I'll try to profile on Android to check if the same behavior is happening, but in the production app only ios reported OOM issues.

@leonardocarvalho
Copy link
Author

@kelset, some impressions after profiling on Android:

  1. Memory increase for each Add in the repro app seems lower on android
  2. Add/remove views seem to have a similar behavior at first, but when GC runs memory is freed and goes back to the original baseline

I was not able to force GC in ios, and given that it holds the memory until the OOM crash I'm not sure it would be helpful anyway. For this reason I'm assuming that the issue is in ios, or at least it's more severe in this platform.

Thanks again for you attention on the matter.

@kelset
Copy link
Contributor

kelset commented Oct 8, 2018

Hey @leonardocarvalho thank you so much for the detailed investigation, I'll show this to the Core since it's a pretty important issue IMHO.

@leonardocarvalho
Copy link
Author

Hi @kelset ,

Did the Core have time to take a look on this issue? If there's anything I could do to help just let me know.

@kelset
Copy link
Contributor

kelset commented Oct 22, 2018

Hey Leonardo, thanks for the ping - sadly noone was able to check it last time I linked it, I'll try again next week since this week there is ReactConf and the JSI is landing too, so a lot is moving.

@miltoneiji

This comment has been minimized.

1 similar comment
@cassiossousa

This comment has been minimized.

@stale
Copy link

stale bot commented Aug 4, 2019

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

@stale stale bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 4, 2019
@stale
Copy link

stale bot commented Aug 11, 2019

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.

@stale stale bot closed this as completed Aug 11, 2019
@facebook facebook locked as resolved and limited conversation to collaborators Aug 12, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. JavaScript Platform: iOS iOS applications. Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests

6 participants