我在 Android 中使用 ListActivity 时出现内存泄漏

Posted

技术标签:

【中文标题】我在 Android 中使用 ListActivity 时出现内存泄漏【英文标题】:I have a memory leak using ListActivity in Android 【发布时间】:2011-04-24 01:17:00 【问题描述】:

我有一个使用服务和一些列表活动的应用程序。当活动打开时,我可以看到 DDMS 中的堆使用量增加,当活动关闭时,堆使用量略有下降。此时服务仍在后台运行。如果通过重新运行应用程序并关闭来再次启动活动,则堆使用量会再次增加然后减少,但永远不会回到活动首次打开之前的原始水平。如果它反复(10-15 次)打开活动然后关闭活动,堆大小(MB 和 # 个对象)会膨胀!

我希望 ListActivity 的 onDestroy 在它被销毁时能够自行处理。我错过了什么?我是否错误地使用了 ListActivity?

类似于我的真实代码的测试应用程序如下。创建一个新的 android 应用程序,将其添加到清单中:

还有这些 java 文件:

泄漏测试活动.java ------------- 包泄漏测试。测试; 导入 java.util.ArrayList; 导入 java.util.HashMap; 导入android.app.Activity; 导入android.app.ListActivity; 导入android.content.Intent; 导入android.os.Bundle; 导入 android.widget.ArrayAdapter; 导入 android.widget.SimpleAdapter; 公共类 LeakActivity 扩展 ListActivity ArrayList> _Data=new ArrayList>(); ArrayAdapter _适配器; /** 在第一次创建活动时调用。 */ @覆盖 公共无效 onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); Intent svc = new Intent(this.getApplicationContext(), LeakTestService.class); 启动服务(svc); // SimpleAdapter 和 ArrayAdapter 都会出现问题 //_Adapter = new SimpleAdapter(this.getApplicationContext(), _Data, android.R.layout.two_line_list_item, new String[] "line1","line2" , new int[] android.R.id.text1, android.R.id.text2 ); _Adapter = new ArrayAdapter(this.getApplicationContext(), android.R.layout.simple_list_item_1, new String[] "data1","data2" ); // 如果删除此行,如果您反复打开+关闭它,堆使用量永远不会膨胀 getListView().setAdapter(_Adapter); @覆盖 公共无效 onDestroy() _适配器=空; // 这行没有帮助 getListView().setAdapter(null); // 这行也没有 super.onDestroy(); 泄漏测试服务.java -------- 包泄漏测试。测试; 导入android.app.Service; 导入android.content.Intent; 导入android.os.IBinder; 导入 android.widget.Toast; 公共类 LeakTestService 扩展服务 @覆盖 公共无效onStart(意图意图,int startId) Toast.makeText(getBaseContext(), "Service onStart", Toast.LENGTH_SHORT).show(); @Override public void onDestroy() Toast.makeText(getBaseContext(), "Service onDestroy", Toast.LENGTH_SHORT).show(); @覆盖 公共IBinder onBind(意图意图) // TODO 自动生成的方法存根 返回空值;

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,但我发现问题只发生在我调试时。在“正常”运行中,所有活动都消失了。

【讨论】:

天哪!你是绝对正确的;我在调试时看到了同样的事情,但无法弄清楚为什么 ListViews 会持续存在。然后我在没有调试器的情况下运行它,一切正常。希望我能在 20 小时前找到你的答案。 天哪,谢谢你的回答。试图追踪与 ListView 相关的内存泄漏一整天,但没有运气。让它在没有附加调试器的情况下运行,它就消失了......【参考方案2】:

如果它反复(10-15 次)打开活动然后关闭活动,堆大小(MB 和 # 个对象)会膨胀!

您是否运行过垃圾收集器? DDMS 中有一个按钮。

一旦你运行了垃圾收集器并且它似乎没有收集任何东西(在 LogCat 中登录),你可以随时转储堆并使用MAT 或jhat 对其进行检查。

我是否错误地使用了 ListActivity?

删除活动中出现的所有getApplicationContext(),并将其替换为this。活动Context,您希望在这些地方使用Activity

同样,我不知道你为什么在Service 中调用getBaseContext(),而不是仅仅使用this,因为Service 也是Context

【讨论】:

同意在声明内存泄漏之前明确运行 GC 几次。您提交的代码中没有任何明显会导致问题的地方。 它说没有什么要收集的原因是因为某个地方的对象即使在它关闭时仍然持有对活动的引用(我认为),这意味着每次活动重新打开时它仍然会持有某处对前一个的引用。我是否使用它、getBaseContext 或 getApplicationContext 都没有关系......同样的气球堆仍然会发生。我在其他地方读到,如果将 Activity 存储在静态变量中,则使用对 Activity 的引用是“泄露上下文”的一种不好的方式,因此避免使用它(我知道我在这里不使用任何静态变量) @kebab:正如我所写,“一旦你运行了垃圾收集器并且它似乎没有收集任何东西(如登录 LogCat),你总是可以转储堆并检查它使用 MAT 或 jhat。”如果有的话,堆分析会告诉你什么仍然保留在活动中。 @CommonsWare:所以我运行了 MAT。我不完全确定如何使用它,但我遇到过这个问题:在“dominator_tree”中,我可以看到很多 android.widget.ListView 类,每次打开和关闭应用程序都有一个类.所以他们肯定在不应该的时候到处闲逛。我原以为 ListActivity 会照顾他们。我应该明确添加代码来删除它们吗? @kebab:好吧,我不得不承认,我从来不需要破解打开的 MAT。但是,根据我在一些截图中看到的内容,您应该能够通过一些鼠标右键选项来辨别 GC 根的路径。请参阅 eclipse.org/mat/about/dominator_tree.png 和 eclipse.org/mat/about/path_2_gc_roots.png。如果我不得不猜测,它们都有一个共同的 GC 根,一旦我们知道那是什么,我们将更接近帮助您弄清楚为什么它们会存在。

以上是关于我在 Android 中使用 ListActivity 时出现内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中使用 ProGuard

我在 Android 中使用 ListActivity 时出现内存泄漏

当我在 android 中使用哈希映射时显示警告(使用新的 SparseArray<String>)

我在 Android 中面临 ClassNotFoundException

Android Studio 不允许我在 Room 中使用 primaryKeys

当我在 LiveData 观察者中使用 navController 时,Android Navigation 组件图停止正常工作