【问题标题】:Bitmaps in AndroidAndroid 中的位图
【发布时间】:2010-12-29 01:03:52
【问题描述】:

我有几个关于位图对象和内存及其一般分类的问题。

  1. 什么是内存位图或本机位图?
  2. 位图内存与堆内存有何不同?

【问题讨论】:

    标签: android bitmap


    【解决方案1】:

    支持 Bitmap 对象的内存是使用本机代码 (malloc()) 分配的,而不是 Java 的 new 关键字。这意味着内存由操作系统直接管理,而不是由 Dalvik。

    本机堆和 Dalvik 堆之间唯一真正的区别是 Dalvik 堆是垃圾收集器,而本机堆不是。

    不过,出于这些目的,这里并没有太大区别。当您的 Bitmap 对象被垃圾回收时,它的析构函数将回收本机堆中的相关内存。

    来源:

    【讨论】:

    • 你知道这个错误是否被修复过吗?我目前在雷电上遇到了同样的问题。我正在回收位图,但它们从未清除本机堆上的任何空间,从而导致 oom 错误。
    • 为了清楚起见,上述答案适用于 android 2.x 及以下版本。从 Android 3 开始,位图实例与堆相反。这可以很容易地检查:在 android 2.x 中创建位图将几乎不影响 Java 堆大小,在 Adnroid 3.x 中创建它会向 Java 堆添加大量字节。
    • 啊!所以你是说计数现在更加直观?太好了 - 除了我们所有编写 hacky 代码来将 android 堆与本机堆相加的人......
    【解决方案2】:

    这里有一个重要的微妙之处:虽然位图像素是在本机堆中分配的,但 Dalvik 中的一些特殊技巧会导致它与 Java 堆相对应。这样做有两个原因:

    (1) 控制应用程序分配的内存量。如果没有计费,应用程序可能会分配大量内存(因为 Bitmap 对象本身非常小,但可以保留任意大量的本机内存),超出 16MB 或 24MB 堆限制。

    (2) 帮助确定何时进行 GC。如果没有会计,您可以分配和释放对 100 个位图对象的引用; GC 不会运行,因为这些对象很小,但实际上它们可以代表大量兆字节的实际分配,这些分配现在没有被及时 GC。通过针对 Java 堆计算这些分配,垃圾收集器将按照它认为正在使用的内存运行。

    请注意,在许多方面,这是一个实现细节;它很可能在未来发生变化,尽管这种基本行为会以某种形式保留,因为这些都是管理位图分配的重要特征。

    【讨论】:

    • 即使在根据 Java 堆计算位图的手机上,本机 malloc 也可以分配超过 16 MiB 的内存。
    • 第一条评论是错误的,所有手机都根据 Dalvik 堆限制计算位图分配。需要注意的主要变化是,从 Android 3.0 开始,分配实际上是在 Dalvik 堆中完成的,因此您不再需要处理 GC 不会像应有的那样积极运行的情况。
    • 你是对的。尽管如此,在处理回收的位图时,GC 还是迟缓了。我发现了不可预知的行为,除非我在回收位图后手动运行 GC(包括下一次“加载”返回回收位图的副本而不是实际从磁盘加载它的趋势)
    • @hackbod ...as of Android 3.0 the allocations are actually done in the Dalvik heap... 所以,recycle() 没用? AFAIK 你不能总是根据命令精确地调用 GC。
    【解决方案3】:

    从野外部署中,我发现了以下设备:

    • 限制为 16 MiB 的 Java 堆的设备(位图几乎没有限制)。
    • 限制为 16 MiB 的设备(Java 堆 + 本机位图存储)
    • 限制为 24 MiB 的 Java 堆的设备(位图几乎没有限制)。
    • 限制为 24 MiB 的设备(Java 堆 + 本机位图存储)

    24 MiB 往往是高分辨率设备,可以使用 Runtime.getRuntime().maxMemory() 检测到。现在也有 32MiB 的设备,一些有根的手机默认有 64MiB。以前,我多次混淆自己,试图弄清楚发生了什么。我认为所有设备都将位图计入堆限制。但是很难对 android 舰队做出任何全面的概括。

    这在 Android 上是一个非常讨厌的问题,而且非常令人困惑。这个限制和它的行为记录很差,很复杂,而且非常不直观。它们还因设备和操作系统版本而异,并且有几个已知的错误。部分问题是限制不精确 - 由于堆碎片,您将在实际限制之前达到 OOM,并且必须保守地留下一兆或两兆缓冲区。更糟糕的是,我有几台设备在您获得 java OOM 异常之前发生了本机段错误(Android 本身中的 100% 错误),这使得永远不会达到限制变得更加重要,因为您甚至无法捕获本机崩溃。有关我的调查的更多详细信息,请查看this post。在同一篇文章中,我解释了如何根据限制衡量使用情况并避免崩溃。

    java堆的大小是Runtime.getRuntime().totalMemory()。

    没有简单的方法来测量本机位图存储的大小。整体原生堆可以使用 Debug.getNativeHeapAllocatedSize() 测量,但只有位图计入限制(我认为)。

    【讨论】:

    • 由于任何人都可以发布他们自己的 Android 版本,因此似乎可以相信存在实施不同策略来限制堆内存的版本,包括您上面描述的那些。例如,CyanogenMod 允许用户自己设置堆限制,所以我不明白为什么没有一个操作系统版本可以实现您描述的有关限制的策略。我很想知道在这方面是否有任何官方指南。如果不是,那么我们就会受制于这些操作系统变体的异想天开。
    • @Carl。据我所知,这纯粹是混乱。虽然一般来说,模组和有根手机往往会增加堆限制,通常为 64M。如此严格限制 Java 堆大小的决定是脑残,并导致糟糕的用户体验。
    • 它确实迫使我们作为开发人员认真审视我们对内存的使用。我目前正在开发一个应用程序,它有一个非常大的数据集,必须完全驻留在堆上(因为搜索过程很密集),而我最初的基于 String 数组的实现占用了 15 MB 的堆仅针对该数据(数组中的每个 String ref 占用 32 个字节的数据)。在几乎达到了许多当前设备仍然强加的 24 MB 限制之后,我设计了一个自定义数据结构,它只需要 3.5 MB 来保存完全相同的信息。现在,我的应用甚至可以在 16MB 限制的设备上运行。
    • @Carl - 我拿了一本包含 180k 单词的字典,平均长度为 10 个字符(15M 存储为 Java 字符串),并将其塞进 400k。 :) 问题是减少位图内存真的很难,因为任何专有存储格式都与本机渲染代码不兼容。那样的话,在具有半个物理内存的设备上人为地制造昂贵的内存似乎非常愚蠢。并且本机代码可以分配 1+G 而不会受到操作系统的抱怨。这只是惩罚 Java 开发人员的一种方式。
    【解决方案4】:

    我们可以通过在清单文件中使用android:largeheap="true" 来增加堆大小。这将解决您的一些问题。

    【讨论】:

    • 想想你的用户电池...请求一个大堆来覆盖内存分配上的一些错误,显然不是一个解决方案。
    猜你喜欢
    • 2012-01-26
    • 1970-01-01
    • 2011-10-10
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 2016-04-11
    • 1970-01-01
    相关资源
    最近更新 更多