0

现象:先在Java端分配一些大内存块,直到遇到OutOfMemoryError,然后全部释放。现在,奇怪的事情发生了:通过 BitmapFactory.decodeXXX(decodeResource, decodeFile, ...) 加载即使是小图片(例如宽度:200,高度:200)也会抛出 OutOfMemoryError!但是现在可以分配任何纯 Java 大对象(例如 new byte[2*1024*1024])!

验证:我写了一些简单的代码来验证问题,可以在这里下载,按“Alloc”按钮多次,你会得到一个OOF错误,然后按“Free All”,现在环境设置好了。现在你可以按“LoadBitmap”,你会发现它在大多数 Android 2.x 手机上都不起作用。(但在模拟器中它还可以,奇怪)

深入挖掘:我尝试深入研究一些 dalvik 代码以找出原因,并在 HeapSource.c 中的函数 externalAllocPossible 中找到一个可能的错误,该错误dvmTrackExternalAllocation 调用,后者在 LogCat 中打印“xxx-byte external allocation too large for this process”消息.

在 externalAllocPossible 中,它简单地写道:

if (currentHeapSize + hs->externalBytesAllocated + n <=
            heap->absoluteMaxSize)
{
    return true;
}
return false;

这意味着一旦本机位图分配大小加上 currentHeapSize(不是实际分配的大小,如下所示,在这种情况下,它保持我们碰撞的堆的最大大小,然后将它们全部释放)超过限制,本机位图分配总是会失败,但是即使 91.3% Java 对象的内存已被释放(设置为 null 并触发 GC),Java 中的 currentHeapSize 似乎也不会减少!

在此处输入图像描述

还有其他人也遇到过这个问题吗?

4

2 回答 2

0

我认为这是正确的。它强制整个应用程序(Java+native)从操作系统中占用不超过一定数量的内存。为此,它必须使用当前的堆大小,因为该内存量仍分配给应用程序(在被 GC 释放时,它不会返回给操作系统,只返回应用程序的内存池)。

无论如何,2.x 早就死了,所以他们不会在那里修复它。他们确实改变了位图在 3.x 和 4.x 中存储内存的方式。最好的办法是先分配所有使用的位图,然后再分配那些大型结构。或者更好的是——将这些大型结构放入一个固定大小的 LRUCache 中,并且不要使用增长直到内存不足的想法,而是仅在需要时加载新数据。

于 2013-06-03T05:07:57.303 回答
0

Bitmaprecycle()方法,描述为:

释放与此位图关联的本机对象...

这种方法背后的原因是有两个堆:Java 堆和本机代码使用的堆。GC 只看到 Java 堆大小;对于 GC,位图可能看起来像一个小对象,因为它在 Java 堆上的大小很小,尽管它引用了本机堆中的一个大内存块。

于 2013-06-03T05:26:27.013 回答