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