10

I'm developing an application with very intense image processing where I have multiple ListFragments in the horizontal FragmentStatePagerAdapter. I aggressively employ practically every trick and suggestion I was able to find here and elsewhere. I download bitmaps and save these to SD and as soft reference memory cache.

Nevertheless as I use the app at certain point I start seeing messages in LogCat just like one below

11-05 15:57:43.951: I/dalvikvm-heap(5449): Clamp target GC heap from 32.655MB to 32.000MB
11-05 15:57:43.951: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 24% free 11512K/14983K, external 17446K/17896K, paused 64ms
11-05 15:57:44.041: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 24% free 11511K/14983K, external 17258K/17896K, paused 77ms

If I continue, messages above will become more urgent

11-05 16:02:09.590: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 71ms
11-05 16:02:09.700: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 23% free 11872K/15239K, external 17497K/17896K, paused 84ms
11-05 16:02:09.720: E/dalvikvm-heap(5449): 192816-byte external allocation too large for this process.
11-05 16:02:09.800: I/dalvikvm-heap(5449): Clamp target GC heap from 33.057MB to 32.000MB
11-05 16:02:09.800: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 68ms
11-05 16:02:09.800: E/GraphicsJNI(5449): VM won't let us allocate 192816 bytes

And inevitably the app will crash with OutOfMemoryException The symptoms are classical memory leak. Yet in my Fragment#onDestroy method I cancel all the pending tasks, unbind and nullify the views and call Bitmap#recycle. Interestingly enough, I do see GC calls in the LogCat but even if I pause for extended period the memory is never reclaimed.

My gut feeling is that it is continuous re-reading of images from SD that causes degradation and inevitable demise

Here's utility cleaning method I'm using trying to shake off drawables (there's more as I said to cancel pending/running tasks and empty ListView adapters)

public static void unbindDrawables(View view, final boolean agressive) {
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i), agressive);
            }
            if (!AdapterView.class.isInstance(view)) {
                ((ViewGroup) view).removeAllViews();
            }
        } else {
            Drawable bmp = view.getBackground();
            if (bmp == null && ImageView.class.isInstance(view)) bmp = ((ImageView) view).getDrawable();
            if (bmp != null) {
                bmp.setCallback(null);
                if (agressive && (TaggedDrawable.class.isInstance(bmp))) {
                    Bitmap bm = ((BitmapDrawable) bmp).getBitmap();
                    if (bm != null) {
                        if (DEBUG) Log.i(TAG, "Forcing bitmap recycle for " + bmp);
                        bm.recycle();
                        bm = null;
                        view.destroyDrawingCache();
                        view = null;
                    }
                }
            }
        }
    }

Needless to say I'm seriously disturbed at this point and will greatly appreciate any suggestions

Bostone
  • 36,858
  • 39
  • 167
  • 227
  • 1
    Take a heap dump using DDMS or the `Debug` class, bring it up in MAT in Eclipse, and see what's going on with your heap. This works best for bitmap-related issues if you run your app on Honeycomb (or, presumably, Ice Cream Sandwich, though I haven't tried that yet). Bitmap memory was largely invisible to MAT pre-Honeycomb, but in Honeycomb they started allocating bitmap memory on the standard (non-native) heap, so you should get better results for figuring out who is holding onto what. – CommonsWare Nov 05 '11 at 23:27
  • 1
    Unfortunately the app is v7+ using v4 compatibility lib. I was also trying to use System.gc() and got into some weird issues like infinite loop where one GC call triggers another one and so on. I also learned that catching OutOfMemoryError is useless at the very best – Bostone Nov 05 '11 at 23:59
  • 2
    "Unfortunately the app is v7+ using v4 compatibility lib" -- so? **Run** your app on Honeycomb or Ice Cream Sandwich for the purposes of tracking down your memory issues. Whatever you fix there will also help you on older Android devices. The leaks aren't changing between versions, but MAT's ability to help you does. – CommonsWare Nov 06 '11 at 00:13
  • Yep, got your point. Doing it right now – Bostone Nov 06 '11 at 01:47
  • Have you tried throwing in [SoftReference](http://developer.android.com/reference/java/lang/ref/SoftReference.html) usage? – kichik Dec 05 '11 at 08:44
  • 1
    Bitmap is the culprit here try to resize it, i already faced this issue u have to work on ur bitmap, bitmap recycle and to make bitmap=null wont work coz u r exceeding the memory heap size before. – PiyushMishra Dec 05 '11 at 12:26
  • @PiyushMishra Done that and it does help. As all other small tricks. But what I can't figure out how is to reclaim memory. I think the fact that memory only deteriorates means that memory is leaking somewhere and I have a sad feeling that it happens in the native code. – Bostone Dec 05 '11 at 21:30
  • 2
    @kichik yes, absolutely. I use WeakReferences exclusively for my in-memory caching – Bostone Dec 05 '11 at 21:31
  • @Droidln: bitmap.recycle wont work at sudden to reclaim d memory coz d reason for this outofmemory is d heap size, At d time of generation of picture it stored in temporary memory of android called heap, a large picture in size surely gives u out of memory, see this link for more info http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android/2299813#2299813 and i think u can't reclaim the memory of device at sudden as u told System.gc() also not working the reason is all references are bind with d activity and their life cycle is similar to activity. – PiyushMishra Dec 06 '11 at 06:50
  • Just a quick couple of thoughts here: have you tried doing this from `onPause()`? I haven't directly experienced this problem with a `Fragment` but I found that this did affect things in an `Activity`—I was trying to free resources from `onStop()` and if `onStop()` was triggered by a second `Activity` going on the back stack, the memory didn't get freed (even though all the code got called). If, of course, your application is putting all these `Fragment`s on the back stack, `onDestroy()` may not be being hit at all. – Andrew Wyld Mar 12 '12 at 15:38

3 Answers3

10

First,GC will not recycle the memory at once you called. Here are some advice from android developer website:

  1. Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)

  2. Try using the context-application instead of a context-activity

  3. Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance

  4. A garbage collector is not an insurance against memory leaks

Second, try to use BitmapFactory.options when you do not very care the quality of the bitmap.

Third, use try catch to process the OutOfMemory exception in catch block.

Finally, Use Memory Analyzer . Open DDMS in Eclipse, in the toolbar there is a update heap button. you can use this to generate a hprof file, then use hprof-conv tool in your andorid-sdk-tools directory to convert the file to the specified format file that Memory Analyzer can read. Now you can use the Memory Analyzer to analysis the possible memory leak. It's really a good tool that will give you many suggestions to avoid outofmemory.

Hope this will help you, If you find some better methods please tell me, i also face the outofmemory in my app.

dreamtale
  • 2,905
  • 1
  • 26
  • 38
  • 1
    _Third, use try catch to process the OutOfMemory exception in catch block._ Never ever, ever do that. It is always a bad idea and the results, at best are unpredictable. I lean toward conclusion that once you get low memory condition your app is pretty much doomed so the best is to concentrate on prevention. For the prevention - your points are valid and valuable but again, the question was "how to recover" – Bostone Dec 07 '11 at 03:38
  • Anyways - I like the content of the answer so I'll mark it as answered unless I get a better one – Bostone Dec 07 '11 at 21:10
  • ^-^, i always wish a better one, OutOfMemory is really a tough problem. – dreamtale Dec 08 '11 at 00:56
0

Try this, where height and width is up to you, currently m using 1024 for both.

public class BitmapUtils {

public static Bitmap resizeBitmap( Bitmap input, int destWidth, int destHeight )
{
    int srcWidth = input.getWidth();
    int srcHeight = input.getHeight();
    boolean needsResize = false;
    float p;
    if ( srcWidth > destWidth || srcHeight > destHeight ) {
        needsResize = true;
        if ( srcWidth > srcHeight && srcWidth > destWidth ) {
            p = (float)destWidth / (float)srcWidth;
            destHeight = (int)( srcHeight * p );
        } else {
            p = (float)destHeight / (float)srcHeight;
            destWidth = (int)( srcWidth * p );
        }
    } else {
        destWidth = srcWidth;
        destHeight = srcHeight;
    }
    if ( needsResize ) {
        Bitmap output = Bitmap.createScaledBitmap( input, destWidth, destHeight, true );
        return output;
    } else {
        return input;
    }
 }
}
PiyushMishra
  • 5,743
  • 6
  • 37
  • 57
  • Sorry but this does not answers the question which is "How to recover from the low memory condition?" Please read bounty note – Bostone Dec 05 '11 at 16:46
0

As Bitmap consumes a lot of memory Call recycle on bitmap when that is no more required.

Also refer this Thread

you won't leak memory if you don't call Bitmap.recycle as long as you don't keep hard references accidentally. You may encounter OutOfMemoryErrors if you try to allocate too many bitmaps at once or too large bitmaps without calling .recycle

Community
  • 1
  • 1
MGK
  • 7,050
  • 6
  • 34
  • 41
  • Thanks but I'm asking specifically how to recover from low memory, not how to not get into it. I do employ the techniques you are mentioning – Bostone Dec 05 '11 at 16:45