对(可能的)Android 内存泄漏一无所知
Posted
技术标签:
【中文标题】对(可能的)Android 内存泄漏一无所知【英文标题】:Clueless About a (Possible) Android Memory Leak 【发布时间】:2014-07-02 09:13:28 【问题描述】:我一直面临着一些烦人的OutOfMemoryErrors
,即使在确保我的所有位图都正确缩放等之后。事实上,这个问题似乎根本与位图无关,但我可能错了。
出于测试和错误隔离的目的,我一直在使用导航抽屉(不使用后退按钮)在两个活动(我们称之为 Main 和 List)之间切换。我可以在 DDMS 中看到,每次返回时分配的内存都会增加大约 180 KB。
我已经完成了内存转储并使用 eclipse MAT 分析了 3 个不同的时间点:
我怀疑内存泄漏,但我无法真正找出原因。根据内存转储,看起来是“剩余”和java.lang.FinalizerReference
在不断增加。 this question 的用户在他的内存转储中也有很多FinalizerReferences
,但答案不是很清楚。
我在上一个时间点所做的泄漏嫌疑人报告并不是很有帮助,因为它怀疑 android.content.res.Resources
和 android.graphics.Bitmap
似乎不会随着时间的推移而增长:
在其中一份报告中(遗憾的是,这里没有出现)我看到 13 个 android.widget.ListView
实例被指出为潜在的泄漏嫌疑人。
这些内存增加发生在活动之间的任何转换(不仅仅是我在本例中使用的 Main 和 List)。
如何找到(不明显?)内存泄漏?很长时间以来我一直在摸不着头脑,所以任何帮助和提示都会很棒。
编辑:
位图 (@OrhanC1):我在上面提到的两个活动中评论了任何 Bitmap
实例化,并且内存仍在增加。内存转储仍然显示一些位图,但我相信它们与资源有关,而不是我分配的实际位图。
关于自定义字体 (@erakitin):我正在使用它们,但我使用单例在我的 Application
上下文 (public class MyApp extends Application
) 中保留每个 Typeface
的单个实例。我已经尝试在上面提到的两个活动中评论对字体的任何引用,但内存仍在增加。
我认为我没有泄露 Context
(@DigCamara):我在这两个活动中没有任何静态引用,我使用的是 Application
上下文而不是 @ 987654341@ 的适配器除外。如果我留在同一个Activity
并进行一些屏幕旋转,内存不会增加。
基于@NickT 的评论:我可以看到我有很多这两种活动的实例。这些内存增加可能是只是后台堆栈活动数量增加的结果,而不是内存泄漏(我虽然操作系统处理了这个问题,apparently not)?如果我使用FLAG_ACTIVITY_REORDER_TO_FRONT
意图标志,那么内存只会增加,直到所有不同的活动都被实例化(一次)。对此事有用:Android not killing activities from stack when memory is low。
【问题讨论】:
您是否在每次转换时都启动新版本的活动(而不是重用后台实例)?也许我对此的回答可能有用:***.com/questions/6835398 完全删除位图会发生什么? 没有看到你的代码,我们只能猜测发生了什么。 @Squonk - 我同意你的观点,但在这种特殊情况下它很复杂:因为我真的不知道问题的根源,所以我不得不发布大量代码。跨度> @OrhanC1 - 我已经评论了两个示例活动中的任何位图创建。内存仍在增加。 【参考方案1】:看起来Remainder
增长的原因是后堆栈中Activity
实例数量的增长(例如,13 个“列表”实例Activity
+ 13 个“主”实例Activity
)。
我已经更改了导航抽屉,以便当用户单击“主页”按钮(将他带到应用程序的“仪表板”)时,我设置了 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
标志:重新使用活动并返回堆栈被清除 (as recommended by the Android guidelines, actually)。事实上,我应该已经这样做了,因为我不想创建 Dashboard(“Home”)活动的多个实例。
通过这样做,我可以看到活动被销毁并且分配的堆大小(包括“Remaining
”切片)减少了:
在修复此问题时,我还注意到我的一个活动和它使用的位图没有被破坏,即使后台堆栈已被清除(泄漏)。在用 MAT 分析后,我得出结论,这个子问题的根源是对我保存在 Activity
中的 ImageView
的引用。通过将此代码添加到 onStop()
方法中,我设法使活动和 Bitmap
都被销毁:
@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);
然后我概括了我所有的Activity
和FragmentActivity
,以便他们在onDestroy()
中调用unbindDrawables(View view)
:
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();
感谢@NickT 为我指明了正确的方向。
【讨论】:
以上是关于对(可能的)Android 内存泄漏一无所知的主要内容,如果未能解决你的问题,请参考以下文章