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

Memory issues when showing a ton of images. #195

Open
DylanVann opened this issue Apr 27, 2018 · 54 comments
Open

Memory issues when showing a ton of images. #195

DylanVann opened this issue Apr 27, 2018 · 54 comments
Labels

Comments

@DylanVann
Copy link
Owner

People have reported memory issues when using this module to show a lot of images.

The example app does work fine showing hundreds of images so I think something else must be at play.

It could be that the images people are showing in these large lists are also all very large images.

I will attempt to replicate this issue and I will summarise findings here.

@DylanVann
Copy link
Owner Author

We probably do need a way to hook into low memory warnings and clear the in-memory cache.

I'm not actually aware of how to get low memory warnings on the JS side of react-native. We might just be able to do it automatically on the native side though.

@varungupta85
Copy link

@DylanVann You may be able to use AppState.addEventListener('memoryWarning', this.handleLowMemoryWarning) to detect low memory on the JS side.

@markrookie
Copy link

markrookie commented Apr 28, 2018

What about not only cache in memory, but also cache in disk. If the memory cache is too much, clear the previous cache and retrieve it from disk cache if necessary.

@DylanVann
Copy link
Owner Author

It already does disk and memory caching. We just need to clear the memory cache on a low memory warning. Actually running out of disk space would be a rare occurrence.

@daqiuqu
Copy link

daqiuqu commented May 16, 2018

I found many OOM errors in my user crash logs after changed ImageCache to fast-image.
Seems add android:largeHeap="true" to AndroidManifest.xml solved this problem.

Sometimes there is memory issue caused by SDWebImage too and I found this issue issue2252. SDWebImage will clear memory cache when receive memory warning, so I am not sure what's the problem here.

@L-Yeiser
Copy link

I had to pull this library out of project because of OOM errors. Even with android:largeHeap="true" I could still get the app to crash. I even managed to generate an OOM error on an iphoneX. If a user uses our app for a substantial amount of time they can load 1000+ images. It would be awesome to get a fix in for this.

@n1ru4l
Copy link
Contributor

n1ru4l commented May 16, 2018

@L-Yeiser Do you unmount FastImage components that are off the screen?

@L-Yeiser
Copy link

@n1ru41 yes. Most of the images are in FlatLists which unmounts components off screen. In one case the images are in a ScrollView but we manually unmount the images off screen.

@daqiuqu
Copy link

daqiuqu commented May 18, 2018

@L-Yeiser I used two android devices with small memory, after set android:largeHeap="true" fixed OOM crash problem, I am going to test this in production mode this week.

Still confused with iOS memory issue.

@iosfitness
Copy link

iosfitness commented May 20, 2018

I am facing the same issue on iPhone in debugging mode. Moreover, the scrolling is also laggy in both debug and release mode.
However, the performance is better on iPad wherein I show a grid of 3 columns and on iPhone I'm showing a grid of 1 column.

One observation, if the number of images is less than there is no issue while scrolling.

Code for reference :

render() {
       Moment.locale('en');
       let documentsList = this.state.documentsList;
       var DeviceInfo = require('react-native-device-info');
       return (
           <View
               onLayout={this._onLayout}
               style={Style.container}>
               {
                   !documentsList && <GiftedSpinner />
               }
               <Spinner
                   visible={this.state.animating}
                   textContent={this.state.title}
                   textStyle={{ color: '#FFF' }}
                   percent={this.state.progress}
                   loadingType={this.state.loaderStyle}
               />
               {

                   documentsList &&
                   <View style={Style.container}>
                       <FlatList
                           data={documentsList}
                           numColumns={DeviceInfo.isTablet() ? 3 : 1}
                           ItemSeparatorComponent={this.renderSeparator}
                           renderItem={this._renderItem}
                           removeClippedSubviews={false}
                           keyExtractor={(item, index) => { return index }}
                          // extraData={this.state.screen_width} //TODO : Why ?
                       />
                   </View>
               }
           </View>
       )
   }

   _renderItem = data => {
       const { item, index } = data;
       let DeviceInfo = require('react-native-device-info');
       let numColumns = DeviceInfo.isTablet() ? 3 : 1;
       let screen_width = this.state.screen_width;
       let screen_height = this.state.screen_height;
       let myWidth = (screen_width) / (numColumns)
       let imageHeight = DeviceInfo.isTablet() ? screen_height / 2 : screen_height / 3;
       let documentUrl;
       if (item.cacheFilePath) {
           documentUrl = `file://${item.cacheFilePath}`;
       } else {
           documentUrl = `${api.BASE_URL}/api/v1/download/fs/${item.output_location}/${item.actual_name}`;
       }

       return (

           <View style={{ margin: 4, overflow: 'hidden', borderRadius: 3, width: myWidth, flexDirection: 'column' }}>

               <TouchableOpacity
                   onPress={this.opendocViewer.bind(this, item)}>
                   <View style={{ padding: 10 }}>
                       <FastImage
                           style={{ width: '100%', height: imageHeight }}
                           source={{
                               uri: documentUrl,
                               priority: DeviceInfo.isTablet() ?FastImage.priority.normal:FastImage.priority.low, //TODO : Test
                           }}
                       // resizeMode={FastImage.resizeMode.cover}
                       />
                       <View style={styles.sectionRow}>
                           <Icon
                               type='font-awesome'
                               color={item.type == 'application/pdf' ? '#F94C3F' : '#0082F4'}
                               name={item.type == 'application/pdf' ? 'file-pdf-o' : 'image'} />
                           <Text style={styles.details}>{item.user_document_name}</Text>
                       </View>
                       <View style={styles.touchable}>
                           <Text style={styles.text_createdon}>Created on :</Text>
                           <Text style={styles.text_createdon}>{Moment(item.created_on).format('DD-MM-YYYY')}</Text>
                       </View>

                       {
                           item.comments !== null && <View style={styles.touchable}>
                               <Text style={styles.text_createdon}>Comments:</Text>
                               <Text style={styles.text_createdon}>{item.comments}</Text>
                           </View>
                       }

                   </View>
               </TouchableOpacity>
           </View>
       )
   }

Will this be related to size of images?. The average size of image is in KBs
@daqiuqu Any alternative of android:largeHeap="true" for iOS
Any inputs will be helpful

@daqiuqu
Copy link

daqiuqu commented May 22, 2018

@iosfitness Use pure component, implement shouldComponentUpdate, don't change state in onLayout. Some tips but I am not sure if these are useful.

@s123121
Copy link

s123121 commented Jun 4, 2018

I used PureComponent, added removeClippedSubviews property inside FlatList, added android:largeHeap="true", still, this happened from time to time (only in Android, iOS is fine)

@flikQ
Copy link

flikQ commented Jun 11, 2018

Also getting this issue, mainly with repetition of the same images in a list (you'd understand if you saw the app as to why there is repetition).

My experience simple, with FastImage enabled the usage jumps from 250MB to 800+ memory usage, it then doesn't release that memory when I go to another area (collection in out case) to repeat the process with different assets.

@flikQ
Copy link

flikQ commented Jun 11, 2018

Could it have anything to do with transparency on PNGs @DylanVann ? A lot of our images that have the most impact are PNGs with transparency, about 80kb each image so hardly large.

@DylanVann
Copy link
Owner Author

Using removeClippedSubviews={false} will prevent some memory from being available.

There was a memory leak in Android that has been fixed in master, although I'm not sure that code is in a production ready state. Hoping to do a new major version on the weekend with it in it though.

@flikQ
Copy link

flikQ commented Jun 20, 2018

@DylanVann sadly setting to false or true makes no difference to the overall performance. Here is a good example - skip the first 20 seconds. https://d.pr/D9WWQN

As you can see the moment we output the cards, the memory usage sky rockets. There is a lot of repetition in assets and each file (player image, card border and card background) are around 80kb tops. This memory isn't released when you navigate away.

If I switch to standard Image rendering (which is visually rubbish as it loads each time) the memory performance is not effected in any way.

It's worth noting that the performance hog on the first 30 seconds was due to the Finish Collection component having a memory leak. This has since been resolved and not connected to Fast-Image issue above. (memory usage is around 250mb now).

Any advice or thoughts would be useful.

@isaachinman
Copy link

isaachinman commented Jul 17, 2018

I just hit this issue myself. Unfortunately, in my case I'm showing a relatively small amount of images (~20) in a FlatList, and then doing some work with FastImage.preload. This crashes my app on an iPhone 6 running iOS 11, and is reproducible 10 times out of 10. I am only seeing crashes on a real iOS device - iOS simulators and all Android emulators/devices work just fine.

Replacing FastImage with RN's standard Image component completely alleviates the issue, and performance is great.

Disappointing, as this project is otherwise absolutely fantastic. I'm glad I caught this before submitting to Apple for review. I'll report back here if I find a temporary solution.

Update: I found a solution to my particular implementation, and thought I'd share as it might shed light on part of the issue. This lib seems to be best suited for one full-size image (1mb+), or lots of thumbnail-size images (~100kb). I'd assume this is a linear relationship.

Notably though, memory seems to spiral out of control even when full-size images with local filesystem URIs are provided. I'm not sure why/how a local file would need to be cached or held in memory - this is probably just a simple bug. I'm aware react-native-fast-image is not (yet) intended for local images, but my understanding was that it was supposed to fallback to a standard Image component if the URI was local. From what I've seen, it's not. Literally a simple ternary fixed perf issues in my case:

{uriIsLocal ?
  <Image
    source={{ uri: imageSource }}
    style={styles.image}
  />
  :
  <FastImage
    source={{ uri: imageSource }}
    style={styles.image}
  />
}

@pilot4u
Copy link

pilot4u commented Jul 19, 2018

I had this issue on iOS as well.
and thanks to @daqiuqu comment regarding

SDWebImage too and I found this issue issue2252.

I managed to fix my crash issue by setting _shouldDecompressImage = NO
it might be helpful if there will be an options to control this setting via info.plist

@isaachinman
Copy link

What is this _shouldDecompressImage flag? What is it actually doing?

@pilot4u
Copy link

pilot4u commented Jul 20, 2018

When SDWebImage downloads image it may save it in memory as extracted format or save as it was received.
e.g. if you download jpg, which is compressed image format, your system need to extract it in order to render the image, but it causes extra CPU cycles.
So using SDWebImage you may choose to save the image in extracted format so decompress cycle will be executed only once.
But it uses large amount of memory, which caused our iPhone 6 test devices to crash.
Therefor, for our case it was better to turn this feature off.
but I had to change it within pods project, which isn't recommended and cannot be committed to git,
So In order to easily use this setting, I proposed to expose this setting to info.plist file so every project may customize this amazing library with needed optimization.

@n1ru4l
Copy link
Contributor

n1ru4l commented Jul 20, 2018

@pilot4u DO you know https://github.com/ds300/patch-package ? I am using this for some libraries 😅

@isaachinman
Copy link

Patching is fine, but if @pilot4u has found something genuinely helpful, it would be good to discuss with @DylanVann and submit a PR.

@daqiuqu
Copy link

daqiuqu commented Jul 20, 2018

Update: After set android:largeHeap="true" in production env, our crash quantity reduced.
Hope @pilot4u 's solution could solve this memory issue in iOS, thanks a lot.

@korgara
Copy link

korgara commented Aug 13, 2018

So, this lib solves some rendering issues, but, unfortunately, provides much worst - memory issues.

I just got some crashes on slow old devices and tried to investigate using AndroidStudio and Xcode profilers.

So, here is what we've got on iPhone6 simulator when all images was rendered by FastImage.
Yeah, it is debug mode, but numbers isn't important.

iphone6-with-fastimage

What is important - is the difference between RN Image and FastImage. Here is the same conditions, but FastImage replaced by RNImage.

iphone6-no-fastimage

On android was the same correlation.

android-galaxys7 no-fastimage

So, I don't know exactly how to fix this, but there should be the way.

@isaachinman
Copy link

@DylanVann Any idea about how we might start work on this? I am more than happy to help.

@henrikland
Copy link

Hi, I ran into this bug today. Showing a list of ~90 images (max image size is about 1.1 MB) causes a crash on iOS. Testing on a few different devices I have access to shows that it crashes 100% of the time on iOS version 11.0.3 and below, but works fine on 11.3 and above. Hope this helps!

@evanjmg
Copy link

evanjmg commented May 4, 2019

@DylanVann is this resolved?

@NoRamadon
Copy link

@DylanVann Any update on this issue? Look like it still happen both on android and ios that run on low-end devices.

@dejavu20180307
Copy link

I am building similar app with infinite scroll of images.
Some suggestions:

  1. You don't need more than 600 * 600 images on a phone especially you are showing thumbnail
  2. Check shouldComponentUpdate and use PureComponent. Extra render makes app runs very slowly.
  3. PNG seems to take more space than JPG, though I saw some post online suggesting using PNG, no idea why is that.

After doing above, I got smooth scroll on debug mode on ios. But if I scroll super fast, after 50 - 100 images, my phone turns hotter and I saw it is starting to lag

@evanjmg
Copy link

evanjmg commented May 29, 2019

This may not be a problem with the library but rather your phone is rendering a lot of items/images at once. A temporary solution for this is to use the maxToRenderPerBatch prop for your lists. This makes it so you only have so much rendered a much. The problem went away when I did this.

@OmarBasem
Copy link

When scrolling through an images flatlist on ios (which is not very long, about 20 images), scrolling becomes laggy and the scrolling position keeps flickering while scrolling. It works fine on Android. Also, it works fine on iOS using normal Image from react-native, but I need to use react-native-fast-image for caching. Also, rendering 4x4 columns of images, so 16 images present on the screen at once causes the app on iOS to crash. It is really disappointing that this library works very smoothly on android, yet totally unusable on iOS.

@evanjmg
Copy link

evanjmg commented Jun 11, 2019

@RAMON90 I don't have this issue when using maxRenderPerBatch and I use a lot more images - have you tried rendering only what you see (recycling)?

@OmarBasem
Copy link

I tried using maxRenderPerBatch and did not work. I am using FlatList so only what on the screen is rendered. I tried using react-native-web-image and still had the same issues, so I guess the problem is with the native implementation of SDWebImage.

@tmjordan
Copy link

@RAMON90 for me, i use the image component from react native only on iOS and the cache is working well.
It can be a workaround until the problem with fast image on ios is fixed.

@yongloon
Copy link

const StyledImage = styled(FastImage)height: 250; width: null; flex: 1;;

Above is what I set the style to my FastImage.

When I set the height to "250", it causes slow performance.
When I set the height to "150", it becomes smooth.

@immortalx
Copy link

immortalx commented Oct 22, 2019

@DylanVann I'm going to share my experience on android with v7.0.2. I'm using an old device a Samsung SM-J200M with 1GB RAM.

For a grid of photos like instagram, 3 images per row, only thumbnails 150x150, my app crashes after scrolling the list for a while, between 1.5k and 2k images.

The app also crashes if after scrolling through a long list of thousands images i send it to background for a minute or so and try to bring it back.

I did the same test with the default Image component and while slower to load images, my app did not crash.

So to compare, i installed the current instagram app and tried to scroll through a large list, after a while the app crashed as well with the error "java.lang.outofmemoryerror"

It's out of my expertise to try to solve this, i just can guess that the images are not being removed from memory after i scroll or something else is being left in memory.

Testing on iPhone 5s, it takes more content to crash but it does too.

@ashokkumar88
Copy link

@immortalx Did you find any solution?

@immortalx
Copy link

@ashokkumar88 Sorry, no workaround in my case yet.

@aibrahim3546
Copy link

I had this issue on iOS as well.
and thanks to @daqiuqu comment regarding

SDWebImage too and I found this issue issue2252.

I managed to fix my crash issue by setting _shouldDecompressImage = NO
it might be helpful if there will be an options to control this setting via info.plist

Where do you set this in the app?

@KarenQiao
Copy link

same issue,any update?

@jbromberg
Copy link

Still a big issue

@trickeyd
Copy link

trickeyd commented Aug 11, 2021

Second job i've come across this issue - last time I moved to a different lib, but that one is depreciated now...

Thought this one may be fixed by now. 😢

Oh well - back to RN Image I guess??

@evanjmg
Copy link

evanjmg commented Aug 12, 2021

2nd job for me this is happening right now as well. Will look into again but may just have to use RN image. Unless people have alternatives?

@trickeyd
Copy link

trickeyd commented Aug 12, 2021

Haha - I think your first and mine was the same @evanjmg - I took over at Velocity after you left 😆

At velocity i ended up migrating to https://github.com/fungilation/react-native-cached-image which worked great - but the maintainer considers it to be depreciated now, with no support after RN 0.61 so too risky to add in latest project.

Right now I'm looking into dumping the memory manually with a fork of this lib and this PR:
#425

I'll let you know if i have any success

@Navipro70
Copy link

Navipro70 commented Sep 16, 2021

I have 46 remote images on one screen, each 2.2MB, onScroll flatlist or on first launch screen, fps on UI thread drops to 1 (iphone 11:). And RAM on initial load is ~300MB, after fetch images RAM is ~1500

@RupamShaw
Copy link

I have 2-3 mb images and about 300 images in flatlist . App keeps on crashing on scroll.
"react-native": "~0.63.4",
"react-native-fast-image": "^8.5.8",

@Navipro70
Copy link

Maybe as a solution is replace png with jpg/jpeg.
For example to provide best image quality for half of a screen of device with retina display we need (SCREEN_WIDTH / 2 * 3)px (I could be wrong).
It can be ~ 2400x2400px and the size of png in this case is ~2bm, but for jpg with 2400x2400px size is 500kb. This will reduce the load for screen.
And also you can try to zip jpg/jpeg + decrease quality (while the image is visible normally) to provide screen without any lags.

@ahartzog
Copy link

ahartzog commented Oct 6, 2021

Also seeing this behavior when scrolling a FlatList using fast-image to display images in tiles as the user scrolls down.

I need to profile in dev, but I know my users are hitting this and I can consistently recreate. Sentry's native handler says...

image

Edit: throttling the scrollEvent and windowSize resolved the crashing behavior for me. My hypothesis is that the cache was being overloaded in some way.

image

@akhilsanker
Copy link

Hi @DylanVann ,

Is this issue resolved? I am facing the same

Thanks

@yanksyoon
Copy link

Any updates on this? It causes app to crash and seems like I need to find another workaround to release to prod.

@flikQ
Copy link

flikQ commented May 18, 2022

My general experience with this error is when a developer is listing many poorly optimised images, I did the same, pulling in our full size images (900x1200) instead of our reduced size versions (300x400). Our experience was immeasurably improved when we considered this as well as managing our FlatLists / SectionLists properly. We've now had this running in app for 4 years and have just released a new app with DC (Batman etc) using this with no performance or OOM issues.

Worth noting in most use cases we have hundreds if not thousands of images being loaded in.

@pjc0247
Copy link

pjc0247 commented Nov 4, 2022

FastImage.defaultSource is totally unusable because of this

#195 (comment)

defaultSource={require('@/assets/img_placeholder.png')}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests