关于(可能的)Android内存泄漏无能为力

use*_*392 24 java android memory-leaks bitmap eclipse-memory-analyzer

OutOfMemoryErrors即使在确保我的所有位图都已正确缩放之后,我也一直面临着一些烦人的问题.事实上,这个问题似乎与Bitmaps根本没有关系,但我可能错了.

出于测试和错误隔离的目的,我一直在使用导航抽屉(不使用后退按钮)在两个活动之间切换(让我们称之为Main和List).我可以在DDMS中看到每次返回时分配的内存增加大约180 KB.

我已完成内存转储并使用eclipse MAT分析3个不同的时间点:

屏蔽1

画面2

屏幕3

我怀疑内存泄漏,但我无法找到原因.根据内存转储,它看起来像是"剩余"并且java.lang.FinalizerReference不断增加.这个问题的用户在FinalizerReferences他的内存转储中也有很多,但答案还不是很清楚.

我在最后一个时间点提出的泄漏嫌疑人报告并不是很有帮助,因为它怀疑android.content.res.Resources并且android.graphics.Bitmap随着时间的推移似乎没有增长:

Screen3LeakReport

在其中一份报告中(遗憾的是,这里没有),我看到13个被android.widget.ListView指出为潜在泄密嫌疑人的实例.

这些内存增加发生在活动之间的任何转换(不仅仅是本例中使用的Main和List).

如何找到(非显而易见的?)内存泄漏?我一直在摸不着头脑,所以任何帮助和提示都会很棒.

编辑:

  • 位图(@ OrhanC1):我已经评论Bitmap了上面提到的两个活动中的任何实例,内存仍在增加.内存转储仍显示一些位图,但我相信它们与资源有关,而不是我分配的实际位图.

  • 关于自定义字体(@erakitin):我正在使用它们,但我使用单例Typeface在我的Application上下文(public class MyApp extends Application)中保留每个字体的单个实例.我已经尝试评论上面提到的两个活动中对字体的任何引用,内存仍然增加.

  • 我不认为我正在泄漏Context(@DigCamara):我没有在这两个活动中持有任何静态引用,我使用Application上下文而不是Activity在适配器中的's.如果我保持相同Activity并进行一些屏幕旋转,则内存不会增加.

  • 基于@ NickT的评论:我可以看到我有两个活动的很多实例.这些内存增加是否只是后端堆栈活动数量增加而不是内存泄漏的结果(虽然操作系统处理的内容显然没有)?如果我使用FLAG_ACTIVITY_REORDER_TO_FRONTintent标志,那么内存只会增加,直到所有不同的活动都被实例化(一次).这个问题很有用:当内存不足时,Android不会从堆栈中查杀活动.

use*_*392 11

看起来Remainder增长的原因是Activity后栈中实例数量的增长(例如"List"的Activity13个实例+"Main"的13个实例Activity).

我已经改变了我的导航抽屉,以便当用户点击"主页"按钮(将他带到应用程序的"仪表板")时,我设置了Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK标志:重新使用活动并清除后面的堆栈(按照建议实际上是Android指南.事实上,我应该已经这样做,因为我不想创建仪表板("Home")活动的几个实例.

通过这样做,我可以看到活动被破坏,分配的堆大小(包括" Remaining"切片)减少:

修复内存增加的问题.

在修复此问题时,我还注意到我的一个活动和它使用的位图没有被破坏,即使后备堆已被清除(泄漏).在用MAT分析后,我得出结论,这个子问题的来源是对ImageView我保留的问题的参考Activity.通过将此代码添加到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);


}
Run Code Online (Sandbox Code Playgroud)

然后我概括我的所有Activity,并FragmentActivity让他们打电话unbindDrawables(View view)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();
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢@NickT指出我正确的方向.