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

[expo-image] When many image is rendered RAM usage goes over 2000mb and app crashes #26781

Open
Miigaarino opened this issue Jan 30, 2024 · 20 comments
Assignees

Comments

@Miigaarino
Copy link

Minimal reproducible example

https://github.com/Miigaarino/expo-image-memory-issue

Summary

Example below is after rendering images on newly created application.
To test:

  1. Clone the repo
  2. Start expo
  3. Go to tab two on app
expo-image.MP4

Environment

expo-env-info 1.2.0 environment info:
    System:
      OS: macOS 14.0
      Shell: 5.9 - /bin/zsh
    Binaries:
      Node: 18.18.0 - /opt/homebrew/opt/node@18/bin/node
      npm: 9.8.1 - /opt/homebrew/opt/node@18/bin/npm
    Managers:
      CocoaPods: 1.12.1 - /opt/homebrew/lib/ruby/gems/3.1.0/bin/pod
    SDKs:
      iOS SDK:
        Platforms: DriverKit 23.2, iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2
    IDEs:
      Android Studio: 2022.3 AI-223.8836.35.2231.10671973
      Xcode: 15.1/15C65 - /usr/bin/xcodebuild
    npmPackages:
      expo: ~50.0.4 => 50.0.4 
      expo-router: ~3.4.6 => 3.4.6 
      react: 18.2.0 => 18.2.0 
      react-dom: 18.2.0 => 18.2.0 
      react-native: 0.73.2 => 0.73.2 
      react-native-web: ~0.19.6 => 0.19.10 
    npmGlobalPackages:
      eas-cli: 5.2.0
      expo-cli: 6.3.10
    Expo Workflow: managed
@Miigaarino Miigaarino added the needs validation Issue needs to be validated label Jan 30, 2024
@expo-bot expo-bot added needs review Issue is ready to be reviewed by a maintainer and removed needs validation Issue needs to be validated labels Jan 30, 2024
@GeorgeHop
Copy link

Confirm! I have same issue and RAM usage goes beyond 6gb, Image component imported from react-native perform properly.

@a-zen
Copy link

a-zen commented Mar 3, 2024

Do you know at how many images it occurs?

@GeorgeHop
Copy link

It can be flatlist or flash
list with 100+ items that use same images...
If you will render many lists in horizontal scrollview with paging and each page will render 100 same images you will see significant drop in performance...
Currently I don't recommend to show images in lists using this lib. use simple react-native Image component.
I tried many props described it the docs and nothing seems to resolve this issue.

@a-zen
Copy link

a-zen commented Mar 3, 2024

Thx for the warning. Was looking at this since expo 50 broke react-native image for me. I guess I need to debug it more 😃

@GeorgeHop
Copy link

Yeah, for some reason simple image from react-native has issues with prefetch on iOS for example.
Waiting when expo will fix issue with RAM because it crashes the app in production 😑

@GeorgeHop
Copy link

Any info on this issue so far?

@Miigaarino
Copy link
Author

Any info on this issue so far?

No, this issue is being ignored for some reason...

@GeorgeHop
Copy link

@brentvatne can you help us please?

@GeorgeHop
Copy link

Any info on this issue so far?

No, this issue is being ignored for some reason...

So I sent mail to @brentvatne and here is his response:

hey george,

could you copy all of those images directly to the repository that you shared there so we can inspect them more easily?

best,
brent

@Miigaarino can you do this?

@expo-bot
Copy link
Collaborator

Thank you for filing this issue!
This comment acknowledges we believe this may be a bug and there’s enough information to investigate it.
However, we can’t promise any sort of timeline for resolution. We prioritize issues based on severity, breadth of impact, and alignment with our roadmap. If you’d like to help move it more quickly, you can continue to investigate it more deeply and/or you can open a pull request that fixes the cause.

@github-actions github-actions bot removed the needs review Issue is ready to be reviewed by a maintainer label Mar 11, 2024
@brentvatne
Copy link
Member

the app does not crash for me but i do see the memory usage spike briefly before recovering. i asked for the images because it would be easy to assess how large each of them are / if there are any big outliers when they are on disk rather than at a url where i would need to open each one to determine the size

Screen.Recording.2024-03-11.at.10.55.00.AM.mov

@brentvatne
Copy link
Member

brentvatne commented Mar 11, 2024

i spoke with @tsapeta about this and he went through the trouble of looking at the images to determine if they could be the cause, he found some that are extremely large and would cause this issue, eg: https://assets-us-01.kc-usercontent.com/6c08ae22-aaba-00c5-2884-7221a976fac0/16abfdeb-34a3-4f47-9764-b72409d9ea44/Innovation%20Experts.jpg - this is a 1.9mb image (reasonable) but its dimensions are 22433 × 6125 (not reasonable). i would suggest ensuring that you use appropriately sized images for the device.

we have a couple ideas to help here:

  • introduce a prop like dangerouslyRenderOversizedImages which could default to false in dev and then warn when image dimensions exceed something like the device width x 5 / device height x 5 (5 being an arbitrary number that i've selected as an example of a possible threshold for warning). you could then choose to enable this prop if you OK with the memory usage implications of this.
  • you could use the allowDownscaling which would increase memory consumption but likely not by as much, however it would be consistent rather than a spike

@Miigaarino
Copy link
Author

So sorry i didn't pay attention for the notifications also for the late reply @GeorgeHop, @brentvatne
Thanks for the suggestions, will try the ideas but in the meantime we are opting out to use Image component from react-native on our production app as we can not resize the incoming images.

@brentvatne
Copy link
Member

@Miigaarino - you could use an image cdn to process the images and serve them if you don't control the source. you could serve a device appropriate sized image and the user would not need to use as much data or memory. if each of those images averages to 500kb and you load 175 then that would be 87.5mb, you could reduce that significantly by serving an image tailored for the device. https://web.dev/articles/image-cdns

@GeorgeHop
Copy link

So I added proposed props...

expo-image situation on real phone connected to xcode, little improvement but I don't use prefetch here.
Screenshot 2024-03-12 at 19 52 07

and here is performance of react-native Image component
Screenshot 2024-03-12 at 19 55 47

component where I render images looks like:
Screenshot 2024-03-12 at 20 00 52

image sample:
3,6 MB: 659823df660f5

Yeah Image is not that small but still it should be saved on the disc only once?! And then render in all items, because the name of image left the same so I wondering how it works then? Also we can apply expo-image-manipulator and add compress 0 for each Image before prefetching, wondering why we can't apply something like that out of the box in prefetch of expo-image.

@tsapeta
Copy link
Member

tsapeta commented Mar 12, 2024

@GeorgeHop Would you be able to provide a simple reproducible example? It looks like your use case (and repro) might be different than the one provided in this issue.

Image is not that small but still it should be saved on the disc only once

In the provided example, the problem is that the exact same image (22433 × 6125) is downloaded at least three times from different URLs. The URL is the cache key, so they are basically stored on the disc and memory separately.

The overall Increased memory consumption with allowDownscaling={false} is expected and it shows that you're definitely using too big images. Keep in mind that the memory needed for the device to render the image is not the same as the file size which is compressed. This really big image is about 2mb in compressed form, but uncompressed (bitmap) it's about 420mb.

Besides using too big images, the problem in expo-image I do see here is the spike for a short period of time when the images are being downscaled.

@GeorgeHop
Copy link

Okay, I will make a little bit more tests...
Also I will prepare some code samples for you.
I will compress all images to below 200kb and probably I will repeat the tests. In this case I guess there should be no spikes and memory issues?

@GeorgeHop
Copy link

Thanks to everyone who trying to help with this 😇
Thank you guys @brentvatne, @tsapeta i really appreciate it. Maybe I will find a lot of new stuff exploring this issue.

@bglgwyng
Copy link

I tried both expo-image and faster-image in a gallery view that renders 100+ images with a size of 1920x1080, 1MB each.
I don't consider it a too pathological case as many gallery-like views would do the same, but both libraries crashed the app with a memory spike.

I wonder why the memory spike is so high. It reaches 2GB for 100 images, which is 20MB per image.
I understand the image is decompressed and stored in memory, but 20MB for a 1MB image seems too much.
Image in react-native loads images slower, but it uses much less memory and works fine with 100+ images. How is it possible? Which difference in the implementation causes this?

Also, I have a question if just throttling the image loading helps in this case.
Throttling the image loading seems trivial, so I suspect that it can affect the user experience as it slows down the image loading, and that's the reason why expo-image and faster-image don't do it.

In my case which renders 100+ images but in a smaller size as thumbnails, so caching the resized images would be helpful.
It doesn't seem that expo-image and faster-image do so.

@Will-Mann-16
Copy link

We were experiencing issues using FlashList (yes we used recyclingKey) here as well, as we were trying to render a social media style feed with a lot of images that we process server side to WebP (roughly 100kb-300kb a pop and reduce quality to around 60%) and we were noticing really huge memory usage spikes on every cache policy we tried apart from none - in the end we reverted to fast-image because it was causing crashes on older iOS devices (iPhone SE 2nd Gen/iPhone XR) and disabled the signed url temporarily (we were using this for the custom cache key). Really interested to see if this is investigated further as we loved the concept but the memory spikes and crashes means we had to revert.

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

No branches or pull requests

8 participants