Clueless About a (Possible) Android Memory Leak
Asked Answered
E

1

24

I’ve been facing some annoying OutOfMemoryErrors, even after making sure that all my Bitmaps are properly scaled etc. In fact, the issue doesn’t seem to be related to Bitmaps at all, but I may be wrong.

For testing and error-isolation purposes, I’ve been switching between two activities (let’s call them Main and List) using my Navigation Drawer (not using the back button). I can see in the DDMS that the allocated memory increases around 180 KB every time I return.

I’ve done memory dumps and used eclipse MAT to analyze 3 different points in time:

Screen1

Screen2

Screen3

I suspect a memory leak but I can’t really find out its cause. According to the memory dumps, it looks like it’s the “Remainder” and java.lang.FinalizerReference that keep increasing. The user in this question also has a lot of FinalizerReferences in his memory dump, but the answer isn't quite clear.

The Leak Suspects Report I made in the last point in time isn’t very helpful as it suspects of android.content.res.Resources and android.graphics.Bitmap which don’t seem to be growing over time:

Screen3LeakReport

In one of the reports (sadly, not present here) I've seen 13 instances of android.widget.ListView pointed as a potential leak suspect.

These memory increases happen with any transition between activities (not just the Main and List I used in this example).

How can I find the (non-obvious?) memory leak? I’ve been scratching my head for a long time so any help and tips would be great.

EDIT:

  • Bitmaps (@OrhanC1): I've commented any Bitmap instantiations in the two activities mentioned above and the memory still increases. The memory dump still shows some bitmaps but I believe they're related to resources and not actual bitmaps allocated by me.

  • Regarding custom fonts (@erakitin): I'm using them, but I'm keeping a single instance of each Typeface in my Application context (public class MyApp extends Application) using a singleton. I've tried commenting any references to the fonts in the two activities mentioned above and the memory still increases.

  • I don't think I'm leaking the Context (@DigCamara): I don't hold any static references inside these two Activities, I'm using the Application context instead of the Activity's except in an adapter. If I stay in the same Activity and do some screen rotations, the memory doesn't increase.

  • Based on @NickT's comment: I can see I have many instances of both activities. Could these memory increases be just the result of an increase in the number of activities of the back stack and not a memory leak (I though the OS dealt with that, apparently not)? If I use the FLAG_ACTIVITY_REORDER_TO_FRONT intent flag then the memory only increases until all different activities have been instantiated (once). Useful for this matter: Android not killing activities from stack when memory is low.

Explosion answered 14/5, 2014 at 10:51 Comment(13)
Are you starting a new version of the activity each transition (rather than reusing a background instance)? Perhaps my answer to this may be useful: stackoverflow.com/questions/6835398Marienthal
What happens when you remove the bitmaps completely?Zoochemistry
Without seeing your code we can only guess what's happening.Implausibility
@Implausibility - I agree with you but in this particular case its complicated: as I don't really know the source of the problem I'd have to post a very large amount of code.Explosion
@Zoochemistry - I've commented any Bitmap creation in the two exemplified activities. Memory still increases.Explosion
@Explosion Are you using custom fonts in your application? I had the same issue before I start "caching" typefaces created from assets.Schoonmaker
@Schoonmaker - I was going to write about that in the question but didn't want to make it any longer: I use custom fonts and I keep the instantiated Typefaces in my Application class, using the Singleton pattern. I'm assuming that's the proper way to do it (?)Explosion
I use custom fonts in navigation drawer and all bitmaps had leaked. Following answer can help me solve this issue: https://mcmap.net/q/54995/-memory-leaks-with-custom-font-for-set-custom-font Try to remove using custom fonts and test again.Schoonmaker
Even though the FontCache looks like a better way of doing it, I already manage my fonts by keeping a single instance in my Application context. I've also commented out the Typeface usage between the two activities, memory still increases.Explosion
From what you posted, it's likely that you're actually leaking the Context. Take a look at this blogpost: curious-creature.org/2008/12/18/avoid-memory-leaks-on-androidHeckelphone
check out this link #8355564 It sounds like there are a few cases where they are using lots of objects that override the object.finalize() method and the thread that processes the queue to finalize these objects can't keep up.Museum
Also if you have lots of redundant activities on the stack maybe try calling finish() on the activity when sending it to a new one to remove it from the stack using startActivityForResult(myIntent, CLOSE_THIS_ACTIVITY); protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if(requestCode == CLOSE_THIS_ACTIVITY) { finish(); } }Museum
Since you're using MAT anyway, how about you do some OQL queries? You have a leak suspect - the ListView. Do a heap.livepaths for them, and try to find any spurious paths that shouldn't be there or you are not cleaning.Carcinogen
E
11

It looks like the reason why the Remainder is growing is the growth of the number of Activity instances in the back stack (e.g. 13 instances of the "List" Activity + 13 instances of the "Main" Activity).

I've changed my Navigation Drawer so that when the user clicks the "Home" button (which takes him to the App's "Dashboard") I set the Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK flags: the Activity is re-used and the back stack is cleared (as recommended by the Android guidelines, actually). In fact, I should have done this already as I don't want several instances of the Dashboard ("Home") activity to be created.

By doing this I can see that the Activities are destroyed and allocated heap size (including the "Remaining" slice) decreases:

Fixing problem with increasing memory.

While fixing this, I also noticed that one of my Activities and a Bitmap used by it were not being destroyed, even if the back stack had been cleared (leak). After analysing with MAT I concluded that the source of this sub-problem was a reference to an ImageView that I kept in the Activity. By adding this code to the onStop() method, I've managed to get both the activity and Bitmap to be destroyed:

@Override
protected void onStop() {
    super.onStop();

    ImageView myImage = (ImageView) findViewById(R.id.myImage );
    if(myImage .getDrawable() != null)
        myImage.getDrawable().setCallback(null);

    RoundedImageView roundImage = (RoundedImageView) findViewById(R.id.roundImage); // a custom View
    if(roundImage.getDrawable() != null)
        roundImage.getDrawable().setCallback(null);


}

I then generalized all my Activity and FragmentActivity so that they call unbindDrawables(View view) in onDestroy():

private void unbindDrawables(View view)
{
    if (view.getBackground() != null)
    {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup && !(view instanceof AdapterView))
    {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
        {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}

Thanks to @NickT for pointing me in the right direction.

Explosion answered 15/5, 2014 at 10:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.