Forum Xamarin.Android

Out of memory exception, despite of dispose

lufo88lufo88 USMember ✭✭✭
edited June 2016 in Xamarin.Android

Hi, I have a problem:
In my app I have a gallery (ViewPager) whichevery fragment contain an ImageView. The image I use in this ViewPager are bytes store in my Sqlite database, previous downloaded from a server.

To inizialize the image I use this code

    public static bool SetImage(this ImageView image, byte[] img, int maxSizeX, int maxSizeY = 0) {
        if (maxSizeY <= 0) {
            maxSizeY = maxSizeX;
        }

        if (img == null) return false;
        var options = new BitmapFactory.Options {
            InJustDecodeBounds = true
        };
        using (BitmapFactory.DecodeByteArray(img, 0, img.Length, options))
        {
            options.InSampleSize = CalculateInSampleSize(options, maxSizeX, maxSizeY);
        }
        options.InJustDecodeBounds = false;

        using (var bitmap = BitmapFactory.DecodeByteArray(img, 0, img.Length, options))
        {
            image.SetImageBitmap(bitmap);
        }
        return true;
    }

CalculateInSampleSize method is defined as

public static int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
var height = options.OutHeight;
var width = options.OutWidth;
var inSampleSize = 1;
if (height <= reqHeight && width <= reqWidth) return inSampleSize;
while ((height / inSampleSize) > reqHeight && (width / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
return inSampleSize;
}

However if I use the gallery 9-10 times the app crash because the application runs out of memory. I need to add an explicit call to GC below the dispose of the bitmap to avoid this problem but this slow down my app.
What can I do to fix this problem?

Posts

  • LjusnanLjusnan DEMember ✭✭✭
    edited June 2016

    Try this one: When you no longer need the Bitmap do Recycle() before disposing it or setting it to NULL. The Recycle method works instantly and you'll have free memory. It is also faster than GC.Collect().

    if (bitmap != null) 
    {
        bitmap.Recycle();
        bitmap = null;
    }
    
  • MihaMarkicMihaMarkic SI ✭✭✭✭

    The problem is that image isn't disposing the assigned bitmap.
    If you can, then do when image is not needed anymore (out of my head)

    Drawable drawable = image.Drawable; image.SetImageBitmap(null); drawable.Dispose(); image.Dispose();

    or, if you can't, do a GC.Collect(1) when you know that image went out of scope. Either way it should release the memory.
    The thing is that Xamarin doesn't know about memory pressure by bitmap (java object) but holds a reference to it (Xamarin sees only the memory used by pointer, i.e. few bytes).

  • lufo88lufo88 USMember ✭✭✭

    @Ljusnan said:
    Try this one: When you no longer need the Bitmap do Recycle() before disposing it or setting it to NULL. The Recycle method works instantly and you'll have free memory. It is also faster than GC.Collect().

    if (bitmap != null) 
    {
        bitmap.Recycle();
        bitmap = null;
    }
    

    I try with recycle. Unfortunately nothing change :-(

    @MihaMarkic said:
    The problem is that image isn't disposing the assigned bitmap.
    If you can, then do when image is not needed anymore (out of my head)

    Drawable drawable = image.Drawable; image.SetImageBitmap(null); drawable.Dispose(); image.Dispose();

    I will try this way.

    or, if you can't, do a GC.Collect(1) when you know that image went out of scope. Either way it should release the memory.
    The thing is that Xamarin doesn't know about memory pressure by bitmap (java object) but holds a reference to it (Xamarin sees only the memory used by pointer, i.e. few bytes).

    I know, that's why I try to dispose the bitmap and avoid the undesiderable call to GC. For me it's a sort of bug, I hope xamarin fix this behavior.

  • MihaMarkicMihaMarkic SI ✭✭✭✭

    I know, that's why I try to dispose the bitmap and avoid the undesiderable call to GC. For me it's a sort of bug, I hope xamarin fix this behavior.

    It is not a bug. It stems from the fact that Xamarin doesn't know Java memory pressure and thus can't react properly.
    Certainly, it'd be great if there was a way to get that info from Java and someday I hope it will be feasible, as it causes all sorts of problems.

  • lufo88lufo88 USMember ✭✭✭
    edited July 2016

    @MihaMarkic said:
    The problem is that image isn't disposing the assigned bitmap.
    If you can, then do when image is not needed anymore (out of my head)

    Drawable drawable = image.Drawable; image.SetImageBitmap(null); drawable.Dispose(); image.Dispose();

    or, if you can't, do a GC.Collect(1) when you know that image went out of scope. Either way it should release the memory.
    The thing is that Xamarin doesn't know about memory pressure by bitmap (java object) but holds a reference to it (Xamarin sees only the memory used by pointer, i.e. few bytes).

    Unfortunately this way not work. The problem is the GC is too lazy so after a few minute the app run out of memory despite the dispose. (dispose mean only the memory is eligible to collect)

  • MihaMarkicMihaMarkic SI ✭✭✭✭

    Perhaps there are other resources stacking up memory? Also try with GC.Collect();

  • lufo88lufo88 USMember ✭✭✭

    @MihaMarkic said:
    Perhaps there are other resources stacking up memory? Also try with GC.Collect();

    No, the problem are the images. In the fragment I check there is not other resource. Some textview, Button, but they are not the problem. I just scroll in theviewpager, after a ten scroll crash
    With cg.collect() this not happen.

  • MihaMarkicMihaMarkic SI ✭✭✭✭

    Which method did you implement that didn't work? The dispose or GC.Collect(1)?

Sign In or Register to comment.