问题
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:
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:
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 myApplication
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 theApplication
context instead of theActivity
's except in an adapter. If I stay in the sameActivity
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.
回答1:
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:
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.
来源:https://stackoverflow.com/questions/23652549/clueless-about-a-possible-android-memory-leak