Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Out of memory error

When I minimize my Android App for about 4 or 5 times, I always get the following error:

02-01 19:24:11.980: E/dalvikvm-heap(22362): Out of memory on a 3686416-byte allocation.
02-01 19:24:12.000: E/dalvikvm(22362): Out of memory: Heap Size=62755KB, Allocated=55237KB, Limit=65536KB
02-01 19:24:12.000: E/dalvikvm(22362): Extra info: Footprint=62435KB, Allowed Footprint=62755KB, Trimmed=2144KB
02-01 19:24:12.000: E/Bitmap_JNI(22362): Create Bitmap Failed.    
02-01 19:24:12.000: E/Bitmap_JNI(22362): Failed to create SkBitmap!
02-01 19:24:12.000: E/AndroidRuntime(22362): FATAL EXCEPTION: main
02-01 19:24:12.000: E/AndroidRuntime(22362): java.lang.OutOfMemoryError: (Heap Size=62755KB, Allocated=55237KB)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.graphics.Bitmap.nativeCreateScaledBitmap(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:744)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.Utils.getResizedBitmap(Utils.java:56)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.MenuView.initialize(MenuView.java:74)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.MenuView$1.handleMessage(MenuView.java:137)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.os.Handler.dispatchMessage(Handler.java:99)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.os.Looper.loop(Looper.java:156)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.app.ActivityThread.main(ActivityThread.java:5045)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at java.lang.reflect.Method.invokeNative(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at java.lang.reflect.Method.invoke(Method.java:511)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at dalvik.system.NativeStart.main(Native Method)

My App consists of only one Activity which has 7 different, self-written Views (all of them containing some Bitmaps) in it, and they are toggled invisible when they are not needed (Probably not good style, but it worked for me until now...). Every one of those Views has a destroy()-function that recycles all the Bitmaps that are used in it, and the onDestroy() of the MainActivity calls all those destroy()-functions. Furthermore, I did not use any static Bitmaps.

So, is there anything else I could try in addition to recycling all Bitmaps and not using static Bitmaps?

like image 817
vauge Avatar asked Dec 27 '22 10:12

vauge


2 Answers

Well. Bitmaps on Android can be a little tricky. Can you give better information about the sources of the bitmaps and their sizes?

Otherwise, I'd recommend looking into these things:

  1. If you're loading remote images, check out fresco. You can also check out Picasso. I personally used to like ImageLoader, but it is not being maintained anymore.

  2. If you're using the inPurgable flag that used to be a recommended option, try to find a way around it as it actually causes more memory to be allocated for each image.

  3. If you decode small, local assets frequently, consider saving your drawables in a hashmap and reusing them when needed. Less GC.

  4. If you care to subclass your Application, you can use the OnLowMemory call to know when you probably really need to clean up (mostly good for debugging, not real life situations) ... If that's not too late... :)

  5. Take a look at Chris Banes' blog. This is a pretty interesting memory cache solution

  6. Implement a memory trimmer you call whenever needed and possible.

  7. One other unsurprising optimization is to use smaller objects when you can... Think of your minimal data models and image sizes and try to have a conforming API for those.

like image 51
Ben Max Rubinstein Avatar answered Feb 12 '23 03:02

Ben Max Rubinstein


For #3 from Ben Max comment I maked two useful classes:

public abstract class SoftReferenceStorage<K, V>{
private static HashMap<Object, SoftReference<Object>> objectsHash = new HashMap<Object, SoftReference<Object>>();

@SuppressWarnings("unchecked")
public V get(K key) {
    if (objectsHash.containsKey(key)) {
        SoftReference<Object> ref = objectsHash.get(key);
        if (ref.get() == null) {
            objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
            return (V)objectsHash.get(key).get();
        } else {
            return (V)ref.get();
        }
    } else {
        objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
        return (V)objectsHash.get(key).get();
    }
}

protected abstract V createValueForKey(K key);
}

and

public class FrequentlyUsedBitmapResources extends SoftReferenceStorage<Integer, Bitmap>{
private static FrequentlyUsedBitmapResources instance = null;

private Resources resources;

public FrequentlyUsedBitmapResources(Resources resources) {
    super();
    this.resources = resources;
}

public static FrequentlyUsedBitmapResources getInstance() {
    if (instance == null) {
        instance = new FrequentlyUsedBitmapResources(HiDriveApp.getContext().getResources());
    }
    return instance;
}

@Override
protected Bitmap createValueForKey(Integer resId) {
    return BitmapFactory.decodeResource(resources, resId);
}
}

can be used like:

Bitmap b = FrequentlyUsedBitmapResources.getInstance().get(R.drawable.overview_photo_placeholder);
like image 27
Fedir Tsapana Avatar answered Feb 12 '23 04:02

Fedir Tsapana