[Xamarin.Forms - Android] Java.Lang.OutOfMemoryError after loading multiple pages with multiple images
See original GitHub issueDescription
Multiple Java.Lang.OutOfMemoryError until the app crashes when loading multiple pages with multiple images. It occurs only on Android, and it seems to concern “older” devices or those who less power, less memory.
Steps to Reproduce
Xamarin Forms app with a main tabbed page. There are 3 pages with one listview in each (each listview is paged, so I use an infinite scroll implementation and use a CacheStrategy = RecycleElement). All listview items contain a small avatar pics loaded with FFImageLoading, and one of these listview displays also one big picture in each item, loaded with FFImageLoading.
All pages load correctly, and display pics. But after navigating between each page, and scroll on some, I got multiple Java.Lang.OutOfMemoryError until the app crashes. If I don’t use FFImageLoading, the problem does not occur but the app becomes slower. It only occurs on Android devices.
Avatar size is approximately 300 to 500 ko. And big pics size is 500 to 1 Mo.
Expected Behavior
No error, and at least the memory and the GC be cleaned.
Actual Behavior
App crashes after loading approximately 50 pics between multiple pages.
XAML
Partial XAML of the ListView ViewCell with avatar and big pics:
<ViewCell BindingContextChanged="Handle_BindingContextChanged">
<!-- other XAML parts -->
<ffimageloading:CachedImage WidthRequest="55" HeightRequest="55" DownsampleToViewSize="true" DownsampleWidth="55" DownsampleHeight="55" ErrorPlaceholder="error.png" LoadingPlaceholder="loading.gif" Aspect="AspectFill" x:Name="PostCreatorImage" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<ffimageloading:CachedImage.Transformations>
<fftransformations:CircleTransformation />
</ffimageloading:CachedImage.Transformations>
<ffimageloading:CachedImage.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenUserProfileCommand}" />
</ffimageloading:CachedImage.GestureRecognizers>
</ffimageloading:CachedImage>
<!-- other XAML parts -->
<ffimageloading:CachedImage x:Name="PostAssetImage" Grid.Row="1" DownsampleToViewSize="true" DownsampleHeight="400" ErrorPlaceholder="error.png" LoadingPlaceholder="loading.gif" MinimumHeightRequest="300" HeightRequest="400" IsVisible="{Binding HasAssets}" Margin="-10, 0" Aspect="AspectFill">
<ffimageloading:CachedImage.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenAssetsCommand}" />
</ffimageloading:CachedImage.GestureRecognizers>
</ffimageloading:CachedImage>
<!-- other XAML parts -->
</ViewCell>
I tried to set only DownsampleToViewSize or only DownsampleHeight/DownsampleWidth ; I tried to add TransformPlaceholders=“false” and FadeAnimations=“false” but no change.
XAML behind code
As you suggested, I load pic with manual set during the OnBindingContextChanged of ViewCell
void Handle_BindingContextChanged(object sender, System.EventArgs e)
{
var cell = sender as ViewCell;
var postViewModel = cell?.BindingContext as PostViewModel;
if (postViewModel == null)
return;
var creatorImage = cell.FindByName<CachedImage>("PostCreatorImage");
if (creatorImage == null)
return;
creatorImage.Source = postViewModel.Post?.CreatorUser?.ThumbnailAvatarUrl;
var cachedImage = cell.FindByName<CachedImage>("PostAssetImage");
if (cachedImage == null)
return;
cachedImage.Source = postViewModel.Asset?.ThumbnailUrl;
base.OnBindingContextChanged();
}
I tried to implement my own CacheKeyFactory. It seemed to be a little bit more efficient but the problem still occurs after.
Android MainActivity and MainApplication
In MainActivity, and in MainApplication, I override OnTrimMemory and OnLowMemory, but when I debug on physical device, it never stops on my breakpoint so it seems these methods are never call:
public override void OnTrimMemory(TrimMemory level)
{
FFImageLoading.ImageService.Instance.InvalidateMemoryCache();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
base.OnTrimMemory(level);
}
public override void OnLowMemory()
{
FFImageLoading.ImageService.Instance.InvalidateMemoryCache();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
base.OnLowMemory();
}
In the App.cs, I tried to set these properties (because I saw you did it in your samples, but I don’t know what are use for):
FFImageLoading.Forms.CachedImage.FixedOnMeasureBehavior = true;
FFImageLoading.Forms.CachedImage.FixedAndroidMotionEventHandler = true;
Logs
First loads when everything is right:
[AbsListView] viewType is heaer or footer
[art] Starting a blocking GC Explicit
[Mono] GC_BRIDGE waiting for bridge processing to finish
[art] Explicit concurrent mark sweep GC freed 987(74KB) AllocSpace objects, 2(2MB) LOS objects, 22% free, 56MB/72MB, paused 683us total 27.404ms
[Mono] GC_TAR_BRIDGE bridges 294 objects 347 opaque 13 colors 294 colors-bridged 294 colors-visible 294 xref 10 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.12ms tarjan 0.42ms scc-setup 0.15ms gather-xref 0.02ms xref-setup 0.02ms cleanup 0.11ms
[Mono] GC_BRIDGE: Complete, was running for 33.09ms
[Mono] GC_MAJOR: (LOS overflow) time 37.74ms, stw 38.63ms los size: 4552K in use: 3436K
[Mono] GC_MAJOR_SWEEP: major size: 4368K in use: 3100K
Thread finished: #12
[AbsListView] viewType is heaer or footer
[art] Starting a blocking GC Explicit
[art] Explicit concurrent mark sweep GC freed 18(1376B) AllocSpace objects, 3(7MB) LOS objects, 17% free, 73MB/89MB, paused 664us total 27.451ms
[Mono] GC_TAR_BRIDGE bridges 278 objects 332 opaque 15 colors 278 colors-bridged 278 colors-visible 278 xref 11 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.12ms tarjan 0.34ms scc-setup 0.17ms gather-xref 0.02ms xref-setup 0.02ms cleanup 0.08ms
[Mono] GC_BRIDGE: Complete, was running for 32.91ms
[Mono] GC_MAJOR: (LOS overflow) time 33.13ms, stw 33.89ms los size: 4552K in use: 3420K
[Mono] GC_MAJOR_SWEEP: major size: 4368K in use: 3085K
[ViewRootImpl] ViewPostImeInputStage processPointer 0
[TextView] setTypeface with style : 0
[TextView] setTypeface with style : 0
[TextView] setTypeface with style : 0
[ViewRootImpl] ViewPostImeInputStage processPointer 1
Thread started: #14
[Mono] GC_BRIDGE waiting for bridge processing to finish
[art] Starting a blocking GC Explicit
[art] Explicit concurrent mark sweep GC freed 687(32KB) AllocSpace objects, 3(4MB) LOS objects, 13% free, 100MB/116MB, paused 778us total 29.272ms
[Mono] GC_TAR_BRIDGE bridges 377 objects 442 opaque 15 colors 377 colors-bridged 377 colors-visible 377 xref 11 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.12ms tarjan 0.52ms scc-setup 0.21ms gather-xref 0.02ms xref-setup 0.03ms cleanup 0.12ms
[Mono] GC_BRIDGE: Complete, was running for 36.25ms
[Mono] GC_MAJOR: (LOS overflow) time 44.17ms, stw 45.30ms los size: 4552K in use: 2687K
[Mono] GC_MAJOR_SWEEP: major size: 4496K in use: 3277K
[ViewRootImpl] ViewPostImeInputStage processPointer 0
And when errors occur:
[art] Alloc partial concurrent mark sweep GC freed 6(192B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 761us total 25.514ms
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 271MB to 256MB
[art] Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 769us total 33.270ms
[art] Forcing collection of SoftReferences for 2MB allocation
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 271MB to 256MB
[art] Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 762us total 33.492ms
[art] Throwing OutOfMemoryError "Failed to allocate a 2560012 byte allocation with 728824 free bytes and 711KB until OOM"
Setting placeholder failed
Java.Lang.OutOfMemoryError: Failed to allocate a 2560012 byte allocation with 728824 free bytes and 711KB until OOM
at Java.Interop.JniEnvironment+StaticMethods.CallStaticObjectMethod (Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00069] in <da9f450baed342f3af31c42cec968688>:0
at Java.Interop.JniPeerMembers+JniStaticMethods.InvokeObjectMethod (System.String encodedMember, Java.Interop.JniArgumentValue* parameters) [0x00018] in <da9f450baed342f3af31c42cec968688>:0
at Android.Graphics.Bitmap.CreateBitmap (System.Int32[] colors, System.Int32 width, System.Int32 height, Android.Graphics.Bitmap+Config config) [0x00077] in <5f142c269d8a438c94480ac03744dec7>:0
at FFImageLoading.Decoders.GifDecoder+PlatformGifHelper.ToBitmapAsync (System.Int32[] data, System.Int32 width, System.Int32 height, System.Int32 downsampleWidth, System.Int32 downsampleHeight) [0x00000] in C:\projects\ffimageloading\source\FFImageLoading.Droid\Decoders\GifDecoder.cs:57
at FFImageLoading.GifHelperBase`1+<SetPixelsAsync>d__54[TNativeImageContainer].MoveNext () [0x00276] in C:\projects\ffimageloading\source\FFImageLoading.Common\Helpers\GifHelperBase.cs:186
--- End of stack trace from previous location where exception was thrown ---
at FFImageLoading.GifHelperBase`1+<ReadBitmapAsync>d__67[TNativeImageContainer].MoveNext () [0x00284] in C:\projects\ffimageloading\source\FFImageLoading.Common\Helpers\GifHelperBase.cs:626
--- End of stack trace from previous location where exception was thrown ---
at FFImageLoading.GifHelperBase`1+<ReadContentsAsync>d__64[TNativeImageContainer].MoveNext () [0x00083] in C:\projects\ffimageloading\source\FFImageLoading.Common\Helpers\GifHelperBase.cs:494
--- End of stack trace from previous location where exception was thrown ---
at FFImageLoading.GifHelperBase`1+<ReadGifAsync>d__56[TNativeImageContainer].MoveNext () [0x00140] in C:\projects\ffimageloading\source\FFImageLoading.Common\Helpers\GifHelperBase.cs:252
--- End of stack trace from previous location where exception was thrown ---
at FFImageLoading.Decoders.GifDecoder+<DecodeAsync>d__0.MoveNext () [0x0004a] in C:\projects\ffimageloading\source\FFImageLoading.Droid\Decoders\GifDecoder.cs:19
--- End of stack trace from previous location where exception was thrown ---
at FFImageLoading.Work.ImageLoaderTask`3+<GenerateImageAsync>d__104[TDecoderContainer,TImageContainer,TImageView].MoveNext () [0x00072] in C:\projects\ffimageloading\source\FFImageLoading.Common\Work\ImageLoaderTask.cs:280
--- End of stack trace from previous location where exception was thrown ---
at FFImageLoading.Work.ImageLoaderTask`3+<ShowPlaceholder>d__108[TDecoderContainer,TImageContainer,TImageView].MoveNext () [0x0037e] in C:\projects\ffimageloading\source\FFImageLoading.Common\Work\ImageLoaderTask.cs:486
--- End of managed Java.Lang.OutOfMemoryError stack trace ---
java.lang.OutOfMemoryError: Failed to allocate a 2560012 byte allocation with 728824 free bytes and 711KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:1048)
at android.graphics.Bitmap.createBitmap(Bitmap.java:1072)
Basic Information
- Version with issue: 2.4.5.860-pre
- Last known good version: I don’t know, I discovered this error after publishing the app for the first time, and I didn’t get this on test devices who are recent and powerful (Samsung Galaxy S8)
- Platform: Xamarin Forms 3.4.0.1009999 / Android (5.1.1, 6.0.1, 7.0 and 8.0)
- Devices : Samsung Galaxy J3 (2016), A3 (2016), Honor 5C (2016), Motorola One Power (2017)
Your plugin is very good. And it works perfectly on iOS. This is my only problem to get a stable app. Hope someone will get an idea.
Thanks for your help !
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:10 (2 by maintainers)
Top GitHub Comments
I could not try on previous version of XF, but I tried by removing FFImageLoading on most of the pages and I did not get OOM anymore. I saw that the memory free space changes but it does not go to OOM.
I also realized that my avatar thumbnail size is big (1000x1000px) while I downsample to 55px or 45px on most of the pages. Maybe it requires to much memory to downsample, even if I also specify the WidthRequest and HeightRequest on the component.
@daniel-luberda, did I do something wrong ?
Thanks for your help
Not exactly. I didn’t try recently with older version. I encountered a similar error few weeks ago on an older version of XF, but it appears on only one device, so I didn’t work on it at that moment. I’m gonna take a look when I can.