Android - 位图与内存管理?

13

我在很多示例中看到开发人员在位图上调用recycle(),然后将其设置为null。 这是为什么呢?垃圾回收器不能负责释放位图吗?

Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
bitmap.recycle();
bitmap = null;
4个回答

17

加入俱乐部吧。它有点道理但不完全正确。

问题在于,在 Android Honeycomb 版本之前,位图的内存是从非托管内存中分配的(现在仍然是这样),这会创建各种问题。它仍然会被释放,但是从位图对象实现的终结器中释放。这意味着至少需要 2 次 GC 才能收集它。此外,如果由于某种原因终结器未能执行 - 你懂的。还有一件事- 它真的很难追踪 - DDMS 看不到它,MAT 也看不到。

对于 Android 3.0,这已经改变了,位图是通过托管的字节数组实现的,但是对于旧手机……


3

bitmap.recycle();释放在位图中使用的本地堆,将其设置为null是为了帮助GC快速收集您的引用。


3
请注意,虽然终结器会为您执行recycle(),但自己调用它可以更快地释放内存,从而减少堆空间不足的可能性。 - CommonsWare
1
是的...还有一件事要注意...在回收位图之前,您需要确保该位图不再使用...否则,在尝试使用已回收的位图时,您将遇到异常。 - Navin Ilavarasan
4
从Android 3.0版本开始,位图不再使用本地堆。 - Romain Guy

1

来自http://developer.android.com/reference/android/graphics/Bitmap.html#recycle%28%29上的文档。


释放与此位图关联的本地对象,并清除对像素数据的引用。这不会同步释放像素数据;如果没有其他引用,它只是允许垃圾回收。该位图被标记为“死亡”,这意味着如果调用getPixels()或setPixels(),它将抛出异常,并且不会绘制任何内容。此操作无法撤消,因此仅在确保没有进一步使用位图时才应调用它。这是一个高级调用,通常不需要调用,因为当没有更多引用此位图时,正常的GC过程将释放此内存。


所以似乎没有必要去调用。我曾经听说过唯一需要手动将对象设置为null的情况是,如果它是静态变量(或某个不容易超出范围的变量)并且您想将其强制从内存中移除。也许,如果您不断地快速分配位图,则可能需要尝试强制进行垃圾回收,但对于大多数情况来说,这可能是不必要的。


1
根据文档,一切似乎都很好,但是有很多情况表明位图引起了OOM。因此,如果您在代码中遇到此问题,一般的解决方法是确保将位图设置为null并从代码中调用gc(是的,我知道这不是最佳方法,也不能保证gc),但这已经是回收一些内存的最后手段了。您还可以尝试使用软引用来进行位图缓存。 - Navin Ilavarasan
我一次性将数百个位图加载到内存中,没有遇到任何问题。我唯一能想到的现代手机上可能会出现的问题是,如果您的位图存在内存泄漏,或者您正在快速分配和丢弃位图(远远超过您一次可以放在屏幕上的数量)。 - onit
请查看以下链接:http://code.google.com/p/android/issues/detail?id=11089,注意Romain Guy的回复。 - Navin Ilavarasan
他的回复并没有支持你需要经常调用recycle的说法。我甚至无法查看发布的代码文件,也没有人提到模拟器设置的任何细节,比如设备RAM大小。Romain Guy的回复实际上似乎支持了我的说法,即只有在快速分配和丢弃位图时才有必要这样做。同样,在我的任何应用程序中都没有这样做过,并且我曾经一次性加载了数百个位图(至少300个大小约为100x100),从未遇到过问题。 - onit
所以,如果您在代码中遇到此问题,一般解决方法是确保将位图设置为null并从代码中调用gc。如果您遇到内存问题,我们需要通知GC收集位图,并且至少需要2-3个GC周期才能实际收集本地堆。更快的方法是通知GC可以立即收集本地堆,因为我的应用程序不再使用它。 - Navin Ilavarasan
在大多数情况下,我们不会一次性加载数百个位图并将其保留在应用程序的范围内(除非在非常特定的情况下)。 - Navin Ilavarasan

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接