Forum Xamarin.Android

Memory Leak in ImageView

I built an Android application that takes a picture and shows the picture. It supports device rotation, so that the ImageView is redrawn with the correct image orientation when the device is rotated. There's a memory leak in here somewhere, and I can't find it. The allocated bitmaps are not being deallocated when the device rotates and the image hierarchy is redrawn. I've done the obvious things mentioned in Xamarin Memory and Performance Best Practices, namely:

using (var bm = BitmapFactory.DecodeFile (uriPath, opts)) {
    using (var rotated = Bitmap.CreateBitmap (bm, 0, 0, bm.Width, bm.Height, rotationMatrix, true)) {
        PictureResult.SetImageBitmap (rotated);
    }
}

MainActivity.cs line 83

My guess is that a reference to the .NET ImageView is being held onto by the .NET VM, which holds onto a reference of the bitmap. The reason I believe this is 1) I am purposively not holding onto a reference of the ImageView in my code, and 2) when I uncomment the second line in this block:

protected override void OnPause ()
{
            base.OnPause ();
            // PictureResult.SetImageBitmap (null);
}

MainActivity.cs line 45

the Bitmap is released from memory on device rotation.

I have attached the following:

  1. Picture of Android Device Monitor showing the allocations of the Bitmap on device rotation
  2. Picture of Android Device Monitor showing Heap growth after a few rotations (~80MB, at ~100MB it crashes)
  3. A zip with the self-contained project

Thank you for helping with this pesky memory problem.

Posts

  • MihaMarkicMihaMarkic SI ✭✭✭✭
    edited June 2015

    The thing is that xamarin is aware only of reference (few bytes) to the native image and won't run a Garbage collection (there is no memory pressure that is aware of) and android GC won't run because they are referenced from xamarin.
    Basically you have to Dispose manually all Bitmap instances once you don't need them anymore - that way android will be able to dispose them. Or run a GC.Collect(1) to force a garbage collection on xamarin side. I'd go with former.

    Also note that you have to dispose the drawable property of ImageView as well.

  • CasperSkouboCasperSkoubo USUniversity, Developer Group Leader

    Handling images uses a lot of memory, especially if you are trying to load a big picture into memory.
    You can scale the image for "show case" to the user.

    secondly, when you set the ImageBitmap(null), you must first, bitmap.Recycle() and bitmap.Dispose(). Otherwise the image will still be around in memory on the Java side, until GC kicks in.

    the reason for it being freed up when you rotate the device, is that the activity is being destroyed and recreated.

  • NathanGuerinNathanGuerin USMember

    @MihaMarkic & @CasperSkoubo -- Thank you for the tips. It is clear that having two VMs with two GCs does complicate the situation a bit. When writing (java) Android code these memory management work-arounds are no longer necessary. Since I keep no reference to the bitmap itself, I could do ImageView.Drawable.Dispose() in OnPause() and I think it would effect the same GC.

  • MihaMarkicMihaMarkic SI ✭✭✭✭

    FYI I did that like this when disposing everything:

            image.SetBackgroundDrawable(d);
            ...
            Drawable drawable = image.Background;
            image.SetBackgroundDrawable(null);
            drawable.Dispose();
            image.Dispose();
    
Sign In or Register to comment.