Android应用程序内存不足问题 – 尝试了一切,仍然无所适从

我花了整整4天的时间来尝试所有能够找出我正在开发的应用程序中的内存泄漏的东西,但很久以前,事情就停止了。

我正在开发的应用程序是社交性的,所以请考虑简介活动(P)并列出活动与数据 – 例如徽章(B)。 您可以从configuration文件跳转到其他configuration文件的徽章列表,其他列表等。

所以想象一下像这样的P1 – > B1 – > P2 – > B2 – > P3 – > B3等stream程。为了一致性,我加载configuration文件和同一用户的徽章,所以每个P页是相同的每个B页面。

这个问题的一般要点是:根据每个页面的大小,在导航一段时间之后,我会在随机的地方(比如地图,string等等)发现内存不足的exception,这似乎并不一致。

在做完所有可以想象的事情之后,为什么我的记忆力已经耗尽,我什么都没有想到。 我不明白的是,为什么Android不会杀死P1,B1等,如果加载时耗尽内存,而不是崩溃。 如果我通过onCreate()和onRestoreInstanceState()返回它们,我预计这些早期的活动将会死亡并被复活。

更别提这个了 – 即使我做了P1 – > B1 – >后退 – > B1 – >后退 – > B1,我仍然崩溃。 这表明某种内存泄漏,但即使在转储hprof并使用MAT和JProfiler之后,我也无法find它。

我已经禁止从网上加载图像(并且增加了加载的testing数据以弥补它,并使testing公平),并确保图像caching使用SoftReferences。 Android实际上试图释放它所拥有的一些SoftReference,但是在它崩溃之前就已经崩溃了。

徽章页从Web获取数据,从BaseAdapter将其加载到一个EntityData数组中,并将其提供给一个ListView(我实际上使用CommonsWare的MergeAdapter ,但在这个Badge活动中,实际上只有一个适配器,但是我无论如何都想提到这个事实)。

我已经通过了代码,无法find任何会泄漏的东西。 我清除了所有可以find的东西,甚至System.gc()左右,但仍然是应用程序崩溃。

我还是不明白为什么没有获得收益的非活动活动,我真的很喜欢弄清楚。

在这一点上,我正在寻找任何提示,build议,解决scheme……任何可能的帮助。

谢谢。

我还是不明白为什么没有获得收益的非活动活动,我真的很喜欢弄清楚。

这不是如何工作。 影响活动生命周期的唯一内存pipe理是跨所有进程的全局内存,因为Android决定内存不足,因此需要杀死后台进程以取回一些进程。

如果您的应用程序在前台开始越来越多的活动,则永远不会进入后台,因此在系统接近终止进程之前,它总是会达到当地的进程内存限制。 (当它杀死它的进程时,它将杀死托pipe所有活动的进程,包括当前处于前台的进程。)

所以在我看来,你的基本问题是:你让太多的活动在同一时间运行,和/或这些活动中的每一个都占用了太多的资源。

你只需要重新devise你的导航,不要依赖堆叠任意数量的潜在的重量级活动。 除非你在onStop()中做了大量的东西(比如调用setContentView()来清除活动的视图层次结构,并清除其它任何可能存在的variables),那么你只会耗尽内存。

您可能需要考虑使用新的Fragment API来用一个更紧密地pipe理其内存的单个活动来replace这个任意堆栈的活动。 例如,如果使用片段的后端堆栈设施,当片段进入后端堆栈且不再可见时,将调用其onDestroyView()方法以完全删除其视图层次结构,从而大大减less了其占用空间。

现在,只要你压在你压回来的stream程中,去一个活动,按回去,去另一个活动,等等,从来没有一个深的堆栈,那么是的,你只是有一个泄漏。 这篇博文描述了如何debugging泄漏: http : //android-developers.blogspot.com/2011/03/memory-analysis-for-android.html

一些技巧:

  1. 确保你不是泄漏活动的上下文。

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

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

  4. 如果你使用内存caching,比如memory-lru,确保它没有占用太多内存。

  5. 不仅图像需要大量的内存,请确保不要在内存中保存太多的其他数据。 这很容易发生,如果你有你的应用程序中的无限列表。 尝试在DataBase中caching数据。

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

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

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

  8. 如果你需要,像无限的导航,那么碎片 – 应该是你的select。 所以你将有1个活动,这将只是切换片段。 这样你也可以解决一些内存问题,比如4号。

  9. 使用内存分析器来找出内存泄漏的原因。
    这是来自Google I / O 2011的非常不错的video:Android应用程序的内存pipe理
    如果你处理位图,这应该是一个必须阅读: 高效地显示位图

位图往往是Android上的内存错误的罪魁祸首,所以这将是一个很好的地方来仔细检查。

你是否持有对每个活动的引用? AFAIK这是一个让Android从堆栈中删除活动的原因。

我们也可以在其他设备上重现此错误? 我经历了一些Android设备的一些奇怪的行为取决于ROM和/或硬件制造商。

我认为这个问题可能是在答案中提到的许多因素的组合,是什么给你的问题。 就像@Tim所说的那样,对某个活动或该活动中某个元素的(静态)引用会导致GC跳过该活动。 这里是讨论这个方面的文章。 我认为可能的问题来自将活动保持在“可见进程”状态或更高的状态,这将大大保证活动及其相关资源永远不会被回收。

我用Service来解决相反的问题,这就是我想到的:有一些东西把你的Activity放在进程的优先级列表上,这样它就不会受到系统GC的影响,比如一个引用(@Tim)或一个循环(@Alvaro)。 循环不需要是一个循环或长时间运行的项目,只是像recursion方法或级联循环(或沿着这些行)的东西。

编辑:据我所知,onPause和onStop是根据需要自动调用Android。 这些方法主要是为了让你在宿主进程停止前保存你需要的东西(保存variables,手动保存状态等)。 但是请注意,明确指出onStop(和onDestroy一起) 可能不会在每种情况下被调用。 另外,如果托pipe进程还托pipe了具有“Forground”或“Visible”状态的Activity,Service等,OS甚至可能不会停止进程/线程。 例如:一个Activity和一个Service都在同一个进程中被拉长,并且Service从onStartCommand()返回START_STICKY ,这个进程至less会自动获得一个可见的状态。 这可能是这里的关键,尝试声明一个新的过程,并看看是否改变任何东西。 尝试将下面的代码行添加到Manifest中Activity的声明中: android:process=":proc2" ,然后再次运行testing,如果你的Activity与其他任何进程共享一个进程。 这里的想法是,如果你已经清理了你的Activity,并且确定这个问题不是你的Activity,那么其他问题就是这个问题。

此外,我不记得我在哪里看到它(如果我甚至在Android文档中看到它),但我记得关于PendingIntent引用一个活动可能会导致一个活动这样的行为。

这里是onStartCommand()页面的一个链接,并提供关于进程非杀戮前端的一些见解。

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

我发现的最大的内存泄漏来源是由一些全球性的,高层次的或长期的对上下文的引用引起的。 如果将“上下文”保存在任何地方的variables中,则可能会遇到无法预料的内存泄漏。

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

在我的情况下真正帮助记忆问题的事情之一最终将我的Bitmap设置为true。 看看为什么我不使用BitmapFactory的inPurgeable选项? 和答案的更多信息的讨论。

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

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