位图、位图.recycle()、弱引用和垃圾回收

25
据我所知,在Android上,建议将Bitmap对象作为WeakReferences引用,以避免内存泄漏。当没有保留位图对象的硬引用时,垃圾收集器会自动收集它。
现在,如果我理解正确,必须始终调用Bitmap.recycle()方法才能释放位图。我认为这是因为Bitmap对象有特殊的内存管理。
这个理解正确吗?
如果是这样的话,在使用WeakReferences时,可能会出现内存泄漏,因为当释放WeakReferences时,Bitmap.recycle()从未被调用。或者说,WeakReferences足以避免内存泄漏吗?
谢谢
1个回答

51

Bitmap.recycle并不是必须调用的,因为垃圾收集器最终会自动清理位图(只要没有引用)。在Android中,位图是在本地内存中创建的,而不是在VM堆上创建的,因此VM堆上实际的位图对象非常小,因为它不包含任何实际的位图数据。(编辑:自Android 3.0+起不再是这种情况) 位图的实际大小仍将计入堆使用量,以便进行垃圾回收并确保您的应用程序不使用过多的内存。

但是,当涉及到Bitmaps时,垃圾收集器似乎有点反复无常。如果你只是删除了所有硬引用,在某些情况下(就像我的情况一样),它会对位图保留一段时间,可能是由于Bitmap对象分配/计数的奇怪方式所致。Bitmap.recycle似乎很适合让GC更快地收集该对象。

无论如何,如果没有保留硬引用,即使没有调用Bitmap.recycle,你也不会 泄漏 内存。但是,如果您尝试一次性分配太多位图或太大的位图而没有调用recycle,则可能会遇到OutOfMemoryErrors

编辑:重要的是要注意,自Android 3.0以来,位图不再在本地内存中分配。它们像其他Java对象一样分配在VM堆上。但是,我所说的不需要调用recycle仍然适用。


清晰的解释。非常感谢! - Sly
1
我以大约20秒的间隔加载图像。我调用recycle(),但Debug.getNativeHeapAllocatedSize()显示本地内存分配不断增长,直到OutOfMemoryError。 - Maxim
对于 Android < 3.0,这完全不正确。如果您使用的是 Android < 3.0,则必须使用 Bitmap.recycle() 回收位图,因为垃圾回收器无法看到存储在 C 数组中的像素数据,而且在使用大量位图的应用程序上最终会耗尽内存。在 Android > 3 上,您不需要调用 recycle,但是您不能在活动范围之外保留任何位图的引用。 - Kevin Parker
4
这段话是在讨论 Android 工程师 Patrick Dubroy 在 2011 年 Google I/O 大会上的演讲。他对于 Bitmap(位图)的内存分配方式提出了异议。在 Android 3.0 之前,Bitmap 的像素数据是分配在本地堆上的(这也是我在原文中提到的),而 Bitmap 的实际大小仍然会计入您的虚拟机堆使用量。但是,只要您没有持有一个硬引用,它们仍然可以被垃圾回收(最终会被回收),此时它们的 finalizer 方法将运行并调用一个本地方法来释放本地堆上的内存。 - Victor
现在,这并不意味着不调用recycle()是一个好主意,因为在3.0之前的Bitmaps中,GC似乎需要经过几次传递才能决定收集它们并调用它们的finalizers。如果在GC正确释放未使用的Bitmaps之前分配了太多或太大的Bitmaps,则可能会遇到OutOfMemoryErrors。但是,我对最初的问题的答案(您必须调用recycle,否则会泄漏内存)仍然是准确的,因为即使您不调用recycle,Bitmaps pre-3.0也将最终被GC回收。 - Victor
请看那里。位图最终将被回收,但它是在终结器中发生的,因此可能需要很长时间才能调用它。而且我认为终结器不能保证运行,所以你可能会遇到OOM。http://androidxref.com/2.1/xref/frameworks/base/graphics/java/android/graphics/Bitmap.java#990 - marekdef

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