Android应用程序内存不足问题 - 尝试了一切但仍然不知所措

Art*_*kii 87 lifecycle android out-of-memory android-activity

我花了整整4天时间尽我所能来弄清楚我正在开发的应用程序中的内存泄漏,但事情很久以前就停止了.

我正在开发的应用程序具有社交性,因此请考虑配置文件活动(P)并列出包含数据的活动 - 例如徽章(B).您可以从配置文件跳转到徽章列表,转到其他配置文件,其他列表等.

所以想象一下这样的流程P1 - > B1 - > P2 - > B2 - > P3 - > B3等.为了保持一致性,我正在加载同一个用户的配置文件和徽章,所以每个P页面是相同的,所以是每个B页面.

这个问题的一般要点是:在导航了一下之后,根据每个页面的大小,我在随机位置得到了一个内存不足的例外 - 位图,字符串等 - 它似乎并不一致.

在做了一切可以想象的事情来弄清楚为什么我的内存不足之后,我什么也没想出来.我不明白的是,如果Android在加载时耗尽内存而不是崩溃,那么为什么Android不会杀死P1,B1等.如果我通过onCreate()和onRestoreInstanceState()返回它们,我会期望这些早期活动会死亡并复活.

更别说这个 - 即使我做P1 - > B1 - >返回 - > B1 - >返回 - > B1,我仍然会崩溃.这表明某种内存泄漏,即使在转储hprof并使用MAT和JProfiler后,我也无法查明它.

我已禁用从Web加载图像(并增加了加载的测试数据以弥补它并使测试公平)并确保图像缓存使用SoftReferences.Android实际上试图释放它所拥有的一些SoftReferences,但就在它崩溃内存之前.

徽章页面从Web获取数据,从BaseAdapter将其加载到EntityData数组中并将其提供给ListView(我实际上使用的是CommonsWare的优秀MergeAdapter,但在此Badge活动中,实际上只有1个适配器,但我无论哪种方式都想提到这个事实).

我已经完成了代码并且无法找到任何可能泄漏的内容.我清除并取消了我能找到的所有内容,甚至是System.gc()左右,但应用程序崩溃了.

我仍然不明白为什么堆栈上的非活动活动不会被收获,我真的很想知道这一点.

在这一点上,我正在寻找任何提示,建议,解决方案......任何有用的东西.

谢谢.

hac*_*bod 106

我仍然不明白为什么堆栈上的非活动活动不会被收获,我真的很想知道这一点.

这不是事情的运作方式.影响活动生命周期的唯一内存管理是所有进程的全局内存,因为Android决定它在内存上运行不足,因此需要杀死后台进程才能获得回复.

如果你的应用正坐在前台开始越来越多的活动,这是从来没有进入的背景,因此会一直打到其本地进程内存限制的系统以往任何时候都接近杀死它的进程.(当它确实杀死它的进程时,它会杀死托管所有活动的进程,包括当前在前台的任何活动.)

所以我觉得你的基本问题是:你让太多的活动同时运行,和/或每个活动都持有太多的资源.

您只需重新设计导航,而不是依赖于堆叠任意数量的潜在重量级活动.除非你在onStop()中做了大量的事情(比如调用setContentView()来清除活动的视图层次结构并清除它可能持有的其他任何变量),你只会耗尽内存.

您可能需要考虑使用新的Fragment API将这个任意堆栈的活动替换为更紧密地管理其内存的单个活动.例如,如果使用片段的后向堆栈功能,当片段进入后台堆栈并且不再可见时,将调用其onDestroyView()方法以完全删除其视图层次结构,从而大大减少其占用空间.

现在,只要你在你按下背面的流崩溃,去一个活动,请按返回,去到另一个活动等,并永远不会有一个深筹码,然后是你只是有一个泄漏.这篇博客文章描述了如何调试泄漏:http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html

  • 在OP的辩护中,这不是文档所暗示的.引用http://developer.android.com/guide/topics/fundamentals/activities.html#Lifecycle"(当活动停止时),不再对用户可见,它可以通过系统时,内存是被杀害在别处需要." "如果一个活动暂停或停止,系统可以从内存中删除它......通过要求它完成(调用它的finish()方法)""(onDestroy()被调用),因为系统正在暂时破坏这个实例节省空间的活动." (47认同)
  • 在同一页面上,"然而,当系统破坏活动以恢复内存时".你所指出的是Android*永远不会破坏回收内存的活动,但只会终止这个过程.如果是这样,这个页面需要进行严格的重写,因为它反复暗示Android会*破坏*一个活动来回收内存.另请注意,许多引用的段落也存在于`Activity` JavaDocs中. (41认同)
  • 它是2013年,文档只是更明确地提出错误观点:http://developer.android.com/training/basics/activity-lifecycle/stopping.html,"一旦你的活动停止,系统可能会破坏它实例,如果它需要恢复系统内存.在极端情况下,系统可能只是杀死你的应用程序进程" (14认同)
  • 我以为我已将此放在此处,但我发布了更新文档的请求:http://code.google.com/p/android/issues/detail?id = 21552 (6认同)
  • 好吧,废话.我一直认为(因为文档)系统管理你进程中停止的活动,并根据需要序列化和反序列化(销毁和创建)它们. (2认同)
  • 非常误导,甚至书籍都在传播这种误解......用漂亮,令人信服的图片http://www.techotopia.com/index.php/Understanding_Android_Application_and_Activity_Lifecycles. (2认同)
  • @ unrealsoul007:"那么为什么在开发者选项中有一个标记``Do not Keep Activity`" - 强制你测试从配置更改中获得的行为(例如,屏幕旋转). (2认同)
  • @unrealsoul007:“但他们坚信 Android 可以获取 Android 文档中所写的后台活动”——欢迎您的同行编写一个演示应用程序来演示他们认为的行为是什么。祝他们为我带来好运。考虑到 Dianne Hackborn 是 Android 核心工程师之一,我怀疑你的同行是否会取得很大成功。 (2认同)
  • @CommonsWare我认为你的这些话能够说服他们.毕竟,CommonsWare对Android有所了解.谢谢!! (2认同)
  • 官方文档不再具有误导性:[_Activity 状态和从内存中弹出_](https://developer.android.com/guide/components/activities/activity-lifecycle#asem) 明确指出“系统永远不会直接杀死 Activity释放内存。相反,它会杀死活动运行的进程” (2认同)

vov*_*kab 21

一些技巧:

  1. 确保您不是泄漏活动背景.

  2. 确保不要保留对位图的引用.在Activity#onStop中清理所有的ImageView,如下所示:

    Drawable d = imageView.getDrawable();  
    if (d != null) d.setCallback(null);  
    imageView.setImageDrawable(null);  
    imageView.setBackgroundDrawable(null);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果您不再需要它们,请回收位图.

  4. 如果你使用内存缓存,比如memory-lru,请确保它没有使用很多内存.

  5. 不仅图像占用大量内存,请确保不要在内存中保留太多其他数据.如果您的应用中有无限列表,则很容易发生这种情况.尝试在DataBase中缓存数据.

  6. 在android 4.2上,有一个带有硬件加速的bug(stackoverflow#13754876),所以如果你hardwareAccelerated=true在你的清单中使用它会泄漏内存.GLES20DisplayList - 继续保持引用,即使你执行了步骤(2)并且没有其他人引用此位图.在这里你需要:

    a)禁用api 16/17的硬件加速;

    b)分离持有位图的视图

  7. 对于Android 3+,您可以尝试android:largeHeap="true"在您的AndroidManifest.但它不会解决你的记忆问题,只是推迟它们.

  8. 如果您需要,如无限导航,那么片段 - 应该是您的选择.所以你将有1个活动,它只会在片段之间切换.这样你也可以解决一些内存问题,比如4号.

  9. 使用Memory Analyzer查找内存泄漏的原因.
    以下是来自Google I/O 2011的非常好的视频:Android应用程序的内存管理
    如果您处理位图,则必须阅读:有效显示位图


Art*_*kii 2

在我的案例中,真正帮助解决内存问题的事情之一最终是将我的位图的 inPurgeable 设置为 true。请参阅为什么我不使用 BitmapFactory 的 inPurgeable 选项?以及答案的讨论以获取更多信息。

Dianne Hackborn 的回答和我们随后的讨论(也感谢 CommonsWare)帮助澄清了我感到困惑的某些事情,所以谢谢你。