在预蜂窝上进行碎片整理/减小堆大小

时间:2013-07-26 16:30:15

标签: android memory-management bitmap

我开发了一个应用程序,它在启动时解析了一些相当大量的JSON,导致堆增长接近其极限。稍后该应用程序正在加载几个位图。在Honeycomb及以上设备上,这些设备似乎使用相同的内存区域进行位图分配以及Java对象分配,这不会导致任何问题,因为JSON解析所需的内存已被释放。但是在预蜂窝设备上它会导致OutOfMemoryErrors

由于堆大小永远不会下降,dalvik似乎无法增加为外部分配(例如位图)保留的内存。

这是崩溃的一个示例日志输出

GC_EXTERNAL_ALLOC freed 601K, 81% free 5504K/27591K, external 4809K/5561K, paused 58ms
586224-byte external allocation too large for this process.

正如您所看到的那样,堆上有足够的内存,但外部分配的内存不多。

有没有办法可以强制dalvik碎片整理并缩小堆?或者我可以强制在堆上发生位图分配而不是为外部分配保留的内存吗?或者是否有任何其他方法可以解决这个我错过的问题?

**更新**

这是更多日志输出(特定设备不记录dalvik-heap消息):

在启动JSON被解析时,dalvik堆增长:

GC_CONCURRENT freed 800K, 19% free 12717K/15687K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 871K, 19% free 13857K/16903K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 1106K, 19% free 14766K/18055K, external 2637K/2773K, paused 3ms+5ms
GC_CONCURRENT freed 818K, 16% free 15946K/18951K, external 2637K/2773K, paused 3ms+6ms
GC_CONCURRENT freed 825K, 15% free 17151K/20167K, external 2637K/2773K, paused 2ms+6ms
GC_CONCURRENT freed 830K, 15% free 18356K/21383K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 814K, 14% free 19519K/22535K, external 2637K/2773K, paused 2ms+6ms
GC_CONCURRENT freed 823K, 13% free 20720K/23751K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 814K, 13% free 21873K/24903K, external 2637K/2773K, paused 3ms+6ms
GC_CONCURRENT freed 813K, 12% free 23016K/26055K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 1771K, 15% free 23205K/27207K, external 2637K/2773K, paused 2ms+5ms

完成之后,大部分堆都会成功释放:

GC_EXPLICIT freed 19207K, 83% free 4735K/27207K, external 2736K/2773K, paused 140ms

此时我有20 MB的免费Dalvik堆空间,稍后开始分配位图:

GC_EXTERNAL_ALLOC freed 254K, 83% free 4814K/27207K, external 2771K/2773K, paused 47ms
GC_EXTERNAL_ALLOC freed 721K, 83% free 4880K/27207K, external 3881K/4131K, paused 50ms
GC_EXTERNAL_ALLOC freed 235K, 83% free 4870K/27207K, external 5398K/5561K, paused 62ms

总设备限制似乎是32 MB dalvik堆需要27 MB,并且永远不会下降所以我用完了外部存储空间来分配位图。

2 个答案:

答案 0 :(得分:0)

首先,你在模拟器上运行吗?因为你的堆大小似乎有点小。根据我的计算,在总共使用了11MB(dalvik + native)之后你就崩溃了,这很少。只是为了验证,崩溃后你有一条与此类似的线吗?

08-22 18:16:24.209: I/dalvikvm-heap(471): Clamp target GC heap from 24.610MB to 24.000MB

设备之间的最大堆大小(dalvik + native)不同。最小值为16MB,屏幕分辨率越大,越大。 480x800的设备通常具有32MB以上的容量。您可以在模拟器的AVD设置中控制堆大小,确保它足够大以反映真实设备。

关于你的问题,你不应该自己支付堆。 VM会自动执行此操作。确实,整个本机堆混乱使GC更难以估计问题,因此您可以获得一些OutOfMemoryErrors,您可以通过在大量分配之前手动运行GC来避免这些错误。

更具体地说,在解析位图时(在BitmapFactory.decodeXXX()之类的调用期间)会发生大量分配。根据我的经验,它实际上有助于在它们之前运行System.gc()。您只需要在蜂窝前设备上执行此操作。

另一个提示是尽快释放本机分配。位图的像素位于本机堆中。它们仅在bitmaps终结器上释放,正如您所知,它需要很长时间才能运行。只有在运行多次系统GC后才会释放此内存。这就是为什么建议您自己手动释放它。只要您完成任何位图,请立即调用Bitmap.recycle()释放其本机内存。

答案 1 :(得分:0)

我想尝试从完全不同的角度来解决这个问题。 Android上的内存问题=该死的本机堆和位图有90%的时间..但这并没有在这里加起来。据我所知,本机堆的局限性很小,可以根据需要增长。我记得的唯一限制是在dalvik GC中进行手动测试,确保您的使用 dalvik堆加上使用的本机堆不会超过您的应用堆限制。

如果你的麻烦与本机堆无关 - 只有你的dalvik堆。

我假设您的500kb失败分配在本机堆中(对于位图像素)。这个分配实际上可能存在于你的dalvik堆中。由于您的堆碎片太多,系统无法在您的dalvik堆中找到连续的500kb块而导致崩溃。

您使用的是哪种位图解码功能?有些方法直接从资源解码到本机堆,有些方法(比如使用字节数组的方法)正在从dalvik堆上的缓冲区解码,这可以解释那里的大量分配。

要解决此问题,请更改位图解码功能并确保它根本不在dalvik堆上进行分配,或者更好的是,让我们在源代码处修复整个碎片问题。

以下是解决碎片问题的一些想法(在开始时在JSON处理部分应用这些):

  1. 在策略性放置的位置手动运行System.gc()。受欢迎的位置是在诸如活动onDestroy之类的大型物体的破坏期间。或者,当您使用大型JSON块完成时,请在其中输入手动GC。

  2. 更快地协助System GC免费资源。这样可以防止堆首先出现碎片。网上有很多关于如何做到这一点的材料。一些来自我的头脑:

    • 如果频繁进行小分配,请尝试采用内存重用方案,例如创建对象池,重用池中的对象而不是分配新对象。这种优化经常出现在Android适配器中(如列表视图中的视图重用)
    • 当您不需要它们时,手动将变量设置为null,以便明确地将它们与参考图形断开,并将它们标记为简易GC(同样可以明确清除数据集合)
    • 当你有大量的操作时避免使用不可变的类,比如字符串的连接,使用StringBuilder而不是常规的String
    • 完全避免终结者
    • 避免循环引用,或至少将其中一个更改为弱引用
    • 在需要时使用弱引用
  3. 你可以通过确保当你只使用一小部分时你的堆没有增长到很大的尺寸来衡量你的成功