关于Android应用程序内存不足问题:Android应用程序内存不足问题 – 尝试了所有内容但仍然处于亏损状态

Android app out of memory issues - tried everything and still at a loss

我花了整整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()左右,但应用程序崩溃了。

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

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

谢谢。


I still don't understand why inactive activities that are on the stack
don't get reaped, and I'd really love to figure that out.

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

如果你的应用程序坐在前台开始越来越多的活动,它永远不会进入后台,所以在系统接近杀死它的进程之前它总是会达到它的本地进程内存限制。 (当它确实杀死它的进程时,它会杀死托管所有活动的进程,包括当前在前台的任何活动。)

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

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

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

现在,只要你在你按下的流程中崩溃,转到一个活动,按回去,转到另一个活动等,从来没有一个深堆栈,然后是,你只是有一个泄漏。这篇博客文章描述了如何调试泄漏:http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html


一些技巧:

  • 确保您不是泄漏活动背景。

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

    1
    2
    3
    4
    Drawable d = imageView.getDrawable();  
    if (d != null) d.setCallback(null);  
    imageView.setImageDrawable(null);  
    imageView.setBackgroundDrawable(null);
  • 如果您不再需要它们,请回收位图。

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

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

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

    a)禁用api 16/17的硬件加速;
    要么
    b)分离持有位图的视图

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

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

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


  • 位图通常是Android上内存错误的罪魁祸首,因此这将是一个需要仔细检查的好地方。


    我认为问题可能是答案中陈述的许多因素的组合,这些都是给你带来问题的。就像@Tim所说的那样,对该活动或该活动中的元素的(静态)引用可能导致GC跳过该活动。这是讨论这个方面的文章。我认为可能的问题来自将Activity保持在"可见进程"状态或更高级别的状态,这几乎可以保证活动及其相关资源永远不会被回收。

    一段时间我用一个服务我遇到了相反的问题,这就是让我继续这个想法的原因:有一些东西让你的活动在进程优先级列表中保持高位,这样它就不会受制于GC系统,例如引用(@Tim)或循环(@Alvaro)。循环不需要是无限的或长时间运行的项目,只是运行很像递归方法或级联循环(或沿着这些行的东西)。

    编辑:据我了解,Android会自动调用onPause和onStop。这些方法主要供您使用,以便您可以在托管过程停止之前处理您需要的内容(保存变量,手动保存状态等);但请注意,明确指出onStop(以及onDestroy)可能不会在每种情况下都被调用。此外,如果托管进程还托管具有"Forground"或"Visible"状态的Activity,Service等,则OS可能甚至不会停止进程/线程。例如:一个活动和一个服务都在同一个进程中,并且服务从onStartCommand()返回START_STICKY,该进程自动至少处于可见状态。这可能是关键,尝试为Activity声明一个新的proc,看看是否有任何改变。尝试将此行添加到Manifest中的Activity声明:android:process=":proc2",然后如果Activity与其他任何内容共享进程,则再次运行测试。这里的想法是,如果你已经清理了你的活动,并且非常确定问题不是你的活动,那么其他问题就是问题,是时候去寻找它了。

    此外,我不记得我在哪里看到它(如果我甚至在Android文档中看到它)但我记得一些关于PendingIntent引用一个Activity可能会导致Activity以这种方式运行。

    这是onStartCommand()页面的链接,其中包含有关流程非杀戮前端的一些见解。


    你是否对每个活动都有一些参考? AFAIK这是让Android不能从堆栈中删除活动的原因。

    我们也能在其他设备上重现此错误吗?我遇到了一些Android设备的奇怪行为,具体取决于ROM和/或硬件制造商。


    在我的案例中真正有助于内存问题的一件事最终是为我的位图设置了可用的真实性。请参阅为什么我不使用BitmapFactory的inPurgeable选项?和答案的讨论更多信息。

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


    尝试将getApplicationContext()传递给需要Context的任何东西。您可能有一个全局变量,它持有对您的活动的引用并阻止它们被垃圾回收。


    我发现的最大内存泄漏源是由某些全局,高级或长期引用的上下文引起的。如果要将"上下文"保存在任何位置的变量中,您可能会遇到不可预测的内存泄漏。


    所以我唯一能想到的就是你有一个直接或间接引用上下文的静态变量。甚至是对应用程序的一部分的引用。我确定你已经尝试过但我会建议以防万一,尝试在onDestroy()中清空所有的静态变量只是为了确保垃圾收集器得到它


    我遇到了同样的问题。 我正在开发一个即时消息应用程序,对于同一个联系人,可以在ChatActivity中启动ProfileActivity,反之亦然。
    我只是在意图中添加一个额外的字符串来启动另一个活动,它获取启动器活动的类类型和用户ID的信息。 例如,ProfileActivity启动ChatActivity,然后在ChatActivity.onCreate中,我标记调用者类类型'ProfileActivity'和用户ID,如果它要启动一个Activity,我会检查它是否是用户的'ProfileActivity'。 如果是这样,只需调用'finish()'并返回前一个ProfileActivity,而不是创建一个新的。
    内存泄漏是另一回事。