Android Honeycomb 中的 Bitmap#recycle() 实际上做了啥?

Posted

技术标签:

【中文标题】Android Honeycomb 中的 Bitmap#recycle() 实际上做了啥?【英文标题】:What does Bitmap#recycle() in Android Honeycomb actually DO?Android Honeycomb 中的 Bitmap#recycle() 实际上做了什么? 【发布时间】:2011-12-12 18:26:57 【问题描述】:

我正在为android Honeycomb 编写一个非常占用内存的应用程序,并且我一直非常小心recycle() 未使用的Bitmaps;事实上,这对于应用程序的工作来说是必要的,因为Bitmaps 不断地循环进出内存。但是,我刚刚在Activity 中实现了onConfigurationChanged(),因此(出于多种原因)我试图将内存释放例程放在onStop() 中。

目前我的onStop() 方法:

设置一些Views 以显示默认Drawable; 在这些Views 以前使用的Bitmaps 上调用recycle(); 空引用Bitmaps。

不幸的是,使用 Eclipse 内存分析器,这似乎对内存使用没有任何影响

正如您所想象的那样,在以一种名义上是垃圾收集的语言来释放资源方面付出了如此多的努力,我本来希望能产生更多的效果。所以我的问题是:recycle() 是做什么的?它是否真的触发了垃圾回收,或者系统会保留内存——即使你调用System.gc()——直到它觉得需要摆脱一些东西?

注意,我知道 Bitmaps 实际上并没有保存在常规堆中,但我认为调用 recycle() 足以确保它们从本机堆中删除。

部分答案

我发现如果ImageView 包含已被回收的Bitmap,则Bitmap 数据仍保留在内存中,直到在ImageView 上调用setImageBitmap(null)。如果调用了setImageResource(...)setImageDrawable(...),甚至可能是这种情况(它们是在一个相对较小的九个补丁中加载的——但是,MAT 分析表明这并没有删除大的Bitmap,它包含在私有ImageView 的成员)。只需在onStop() 调用此函数即可从我们的应用程序堆中剔除大约 10MB。不过,显然这可能不适用于 Honeycomb 之前的 Android 版本。

【问题讨论】:

我有。实际上,我单步执行了回收它们的代码,并在调试器中检查了isRecycled(),它返回了true 哦,我明白你的意思了,对不起。 (漫长的一天。)我的意思是,现在或将来某个时候,当 Android 感觉像它时,内存会被释放吗? 【参考方案1】:

回收释放分配给位图的本机内存。实际的 Bitmap 对象将保留在 Dalvik Heap 中,直到下一次垃圾回收(但该对象占用的内存微不足道)。

据我所知,确实没有办法转储本机堆。因此,您将无法查看位图的本机数据是否通过堆转储消失了。但是,您应该会看到应用程序使用的内存总量减少了。这个question 应该可以帮助您发现访问应用程序内存使用统计信息的各种方法。

【讨论】:

这实际上是一个好点......当我运行这个时,Dalvik 堆上的内存使用量正在上升,这与我对 Bitmap 本机对象的预期数量成正比,所以大概是这个 Dalvik 使用在Views 内部?如果是这样,我如何从Views 中释放它? 我很难说为什么你的内存使用量会增加,因为这可能是由于很多事情。此外,垃圾收集器不会持续运行,因此您真正需要担心的唯一情况是,如果在 GC 之后,数量不会大幅下降。如果您使用的是带有 Honeycomb 或 ICS 仿真器的平板电脑,则位图内存应显示在 dalvik 堆中。 这正是我担心的!我知道你的意思,你显然不可能知道我在做什么,但实际上它只是ImageViews 包含Bitmaps,所有这些都来自BitmapFactoryBitmapRegionDecoder。 DDMS 验证大部分使用来自那里(每个 Bitmap 往往约为 3MB)。 recycle() 有时会工作,否则事情会崩溃(就像我说的......很多自行车进出!) 在回收调用之后,如果您通过 DDMS 选项卡强制进行 GC,Bitmap 是否会从您的堆中删除?如果没有,您可以使用 Eclipse MAT(内存分析器工具)查看是什么对象使位图保持活动状态。 不,我试过了,它一直在那里。 MAT 似乎是个不错的选择,谢谢 :)【参考方案2】:

正如贾斯汀所说,位图数据不在虚拟机堆中分配。在VM heap(很小)中有对它的引用,但实际数据是由底层Skia图形库分配在Native heap中的。 [请注意,这可能在以后的 Android 级别中发生了变化,但对于 2.1 和 2.2 来说是正确的] 当您执行 recycle() 将 VM 堆中的一小部分和本机堆中的实际数据标记为空闲且可用于 GC 时。但实际的收集是由两种不同的 GC 机制执行的。 VM 堆中的部分由 Davlik GC 收集 - 您可以通过 DDMS 看到发生的情况。但是本机堆数据是由 Skia GC 收集的,它似乎更懒惰(它运行的频率更低?)。这意味着,即使使用严格的 recycle()s,也有可能领先于原生堆 GC。 幸运的是,有一些机制可以监视本机堆的状态。见BitmapFactory OOM driving me nuts。

【讨论】:

我已经实现了一些东西来解决本机堆问题(包括一个 AsyncTask 来加载 Bitmaps,它从 BitmapFactory 捕获 OutOfMemoryError 并等待长达 2 秒再试一次)但在这种情况下,内存似乎位于ImageView 对象内。打电话给setImageBitmap(null) 以及setImageResource(resourceID),虽然听起来很疯狂,但效果不错.... 还有几点:这里的问题不是内存没有被快速回收(我已经有等待循环处理这个问题),而是它没有被回收。正如我现在发现的那样,ImageView 使Bitmap 记忆保持活跃;调用setImageBitmap(null) 解决了这个问题(令人惊讶的是,调用setImageDrawable(...)setImageResource(...) 似乎不足以从内存中删除Bitmap — 显式空值已经从onStop() 的配置文件中剔除大约10MB)。 您有关于此 Skia 图形垃圾收集的任何文档吗?我从来没有听说过/读过任何关于这个的东西,所以这将是一个很好的信息。它是一个原生库,所以那里似乎不太可能有任何类型的垃圾收集。 你是对的。 Skia 是一个本地 (C++) 库,在 stdlib 内存管理上拥有自己的堆管理。称其为“GC”对我来说有点草率。至于文件,我不知道;我只是在必要的地方阅读代码 recycle() 方法应该处理原生的Bitmap 内存,但正如问题所述,事实并非如此。我还发现onStop() 不是调用回收或位图归零代码的好地方,因为当您开始一个新的活动时,活动方法的执行顺序是Activity1.onPause()Activity2.onCreate()Activity2.onStart(), Activity2.onResume(), Activity1.onStop() 这意味着如果你从onStop() 释放它,如果另一个活动已经开始,内存实际上并没有被释放。【参考方案3】:

我发现,从 Honeycomb 开始,如果 ImageView 包含已回收的 Bitmap,则 Bitmap 数据仍保留在内存中,直到在 ImageView 上调用 setImageBitmap(null)。如果调用setImageResource(...)setImageDrawable(...),甚至可能是这种情况(在这种情况下,一个非常大的位图被一个相当小的九补丁替换,但只有在加载九补丁之前调用setImageBitmap(null)实际释放的内存)。

【讨论】:

Andrew,您是使用 Honeycomb 还是 ICS 进行开发?这对阅读此问题的其他 SOers 会有所帮助,因为它确实会产生重大影响。您所描述的行为我希望只在 Honeycomb 或更高版本中看到。另外,请参阅有关调用 System.gc() 的 Android 文档。建议调用该函数绝不是一个好主意。 GC 几乎总是知道运行的绝对最佳时间。 我删除了对 System.gc() 的引用——是的,我们正在运行 Honeycomb。我将编辑帖子以解释这一点。 已编辑。感谢大家的帮助! 一个重要提示:在 ICS 中对位图显式调用 recycle() 可能会导致致命的崩溃。典型 - 您必须在早期版本中回收以避免内存泄漏,并且您不得在 ICS 之后回收以避免崩溃。谢谢,谷歌... @Adrian,你有更多的细节吗?我有一个应用程序在许多不同的 ICS 设备上运行,使用了 recycle() 并且没有看到崩溃。如果你有这方面的一些信息,我很乐意看到它以确保我没有做危险的事情。

以上是关于Android Honeycomb 中的 Bitmap#recycle() 实际上做了啥?的主要内容,如果未能解决你的问题,请参考以下文章

Android Honeycomb:如何更改 FrameLayout 中的片段,而不重新创建它们?

Android Honeycomb:如何更改 FrameLayout 中的片段,而不重新创建它们?

Android:BitmapFactory.nativeDecodeAsset 处的 OutOfMemoryError(Honeycomb 3.0 及更高版本)

Honeycomb 下的 Android 不支持 Bytebuffer array() 方法

如何获取 android Honeycomb 系统的屏幕宽度和高度?

Android HoneyComb DatePicker 文本颜色