Open Tom3652 opened 2 years ago
Thanks for the report @Tom3652
Can we know what's the image resolution is being used ?
Also, can you try by using cacheWidth
, cacheHeight
inside Image.Network
and see if it makes any difference ?
Hi @darshankawar, yes it's in the URL actually 1500x1500.
However this is only to demonstrate the bad behavior that i am using higher resolution images, if you set a lower cacheWidth
it will only delay the problem but the OOM is inevitable if you have an infinite list of photos and scrolling down.
In previous versions of flutter (especially 2.10.x
), the usage of PaintingBinding.instance.imageCache.maximumSizeBytes
in my app allowed me to keep the same memory usage while scrolling this infinite photo list.
Check this comment and issue for your reference : https://github.com/flutter/flutter/issues/110331#issuecomment-1230505200
I have checked the provided link and the comment doesn't help unfortunately. In my real app, i am display veeery small resolution pictures (thumbnails 15-30kB) each, but after scrolling through hundreds of items, i run into OOM.
The comment you mention is only about delaying the real issue. The real issue is that flutter doesn't release image from memory when they are no longer on the screen.
If it was the case, in my very simple sample code, you would be at 30MB after going back from the ListView
with Navigator.pop(), which isn't the case.
Also, the comment you mentioned is not talking about infinite list of items. It's just a reminder to not load an image in 16000x16000 in memory when the phone can only display 1920x1080 for example, which makes perfect sense & it's normal that if users are trying to display nonsense image resolution, the OOM will come with 3 images and lags etc... So i am guessing that no one is doing so because it doesn't make any sense.
Besides, in my example code above, i didn't put high resolution images, 1500px isn't even full HD and is lower than Instagram quality display to take a real-world example. And Instagram doesn't go in OOM when displaying infinite list of Items with higher resolution than i do. I simply would like Flutter to do the same as React Native on this one.
But to come back to this current issue, there is something changed in the PaintingBinding.instance.imageCache.maximumSizeByte
that make it useless now unfortunately, and that was the issue i was pointing out.
There are plenty other issues about OOM in Image
widget and i don't want to duplicate, i have already made some myself last year.
Thanks for the feedback @Tom3652. Curious to know if the memory usage is also same in release
and profile
mode ?
Because, if you are running in debug mode, the memory usage is on higher side as compared to in other modes. Can you try by running in --profile
mode and share the observation to see if it is still high ?
Hi @darshankawar !
I have tried in profile
mode and i do see the same behavior, with a lower amount of RAM used with lower resolution pictures of 500x500.
The images are not cleared from memory while scrolling the ListView.builder
either. The images no longer on the screen should be evicted from memory using the above method.
You can see the same behavior by running this sample, click on start
button and see the memory in the dev tools only increasing, just wait and see the bad behavior.
You will also see that popping the page will not release the memory :
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
PaintingBinding.instance.imageCache.maximumSizeBytes = 1024 * 1024 * 30;
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => const MaterialApp(home: Home());
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
Future<void> openGallery(BuildContext context) {
return Navigator.of(context).push(MaterialPageRoute(builder: (_) => Gallery()));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: const Text("Start"),
onPressed: () => openGallery(context),
),
),
);
}
}
class Gallery extends StatelessWidget {
final String imageURL = 'https://picsum.photos/500?image=';
final int maxImageID = 75;
final int columns = 2;
final ScrollController controller = ScrollController();
Gallery({Key? key}) : super(key: key);
void animateToOffset(double offset) {
if (offset > controller.offset) {
Future.delayed(const Duration(seconds: 1)).then((_) {
controller.animateTo(
offset,
duration: const Duration(seconds: 1),
curve: Curves.easeInOut,
);
});
}
}
@override
Widget build(BuildContext context) {
double imageSize = MediaQuery.of(context).size.width / columns;
return Scaffold(
appBar: AppBar(title: const Text("Gallery")),
body: GridView.builder(
controller: controller,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: columns),
itemBuilder: (context, index) {
return Image.network(
"$imageURL${index % maxImageID}",
fit: BoxFit.cover,
height: imageSize,
width: imageSize,
frameBuilder: (context, child, frame, _) {
if (frame == null) return child;
animateToOffset(imageSize * (index ~/ columns));
return child;
},
);
},
),
);
}
}
Thanks for the update. Running code sample above specially in profile
and release
mode on iOS, I am getting same behavior as reported.
The images being higher resolution don't seem to release memory which seems to be adding in total memory usage.
Maybe similar / related to:
https://github.com/flutter/flutter/issues/110331 https://github.com/flutter/flutter/issues/108928
Thanks for verifying. The links you provided are with loading high resolution images. I just want to specify that in the example you just run, the images are in 500x500 which is even less than half of the device screen resolution usually.
And i have picked 500x500 photos to show you the behavior faster, but in my real-world app i am loading pictures 200x200 which are only thumbnails and the app crashes eventually at 100% :(
I'm also experiencing this same issue
Same, but on Windows, not ios
Steps to Reproduce
The title is actually wrong : the setter is working (i have logged it) but the actions that should
evict
and release images from memory after setting the value are not working.start
buttonExpected results:
In previous version of Flutter ( <
3.0.x
), when usingPaintingBinding.instance.imageCache.maximumSizeBytes = 1024 * 1024 * 30
it would evict any images from memory cache to maintain the cap at 30MB of memory usage.In this example, the memory usage of step 5 should be equal at step 2 ideally. But if for any reason it would not be the case, the memory of step 5 should NOT be higher than
memory_step2_value + 30MB
thanks to thePaintingBinding.instance.imageCache.maximumSizeBytes
setter.Actual results:
The memory usage is much higher.
In my case it's + 140MB :
Against 30MB initially :
This is a real issue because this was my workaround to counter #102140 and #62107 and it's not working anymore.
The real issue is the above one because images are not released from memory or at least in a very bad / long way.
Code sample
```dart import 'package:flutter/material.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); PaintingBinding.instance.imageCache.maximumSizeBytes = 1024 * 1024 * 30; runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) => const MaterialApp(home: Home()); } class Home extends StatelessWidget { const Home({Key? key}) : super(key: key); FutureLogs
``` [✓] Flutter (Channel stable, 3.3.0, on macOS 12.5.1 21G83 darwin-x64, locale fr-FR) • Flutter version 3.3.0 on channel stable at /Users/foxtom/Desktop/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision ffccd96b62 (3 days ago), 2022-08-29 17:28:57 -0700 • Engine revision 5e9e0e0aa8 • Dart version 2.18.0 • DevTools version 2.15.0 [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) • Android SDK at /Users/foxtom/Library/Android/sdk • Platform android-33, build-tools 33.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.4.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13F100 • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.70.2) • VS Code at /Users/foxtom/Desktop/Visual Studio Code.app/Contents • Flutter extension version 3.46.0 [✓] Connected device (3 available) • Now You See Me (mobile) • 00008020-001204401E78002E • ios • iOS 15.6.1 19G82 • macOS (desktop) • macos • darwin-x64 • macOS 12.5.1 21G83 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 104.0.5112.101 [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ```