我有几个关于位图对象、内存和它们的一般分类的问题。
- 什么是内存或本地位图?
- 位图内存与堆内存有何不同?
用于支持 Bitmap 对象的内存是使用本地代码 (malloc()
) 分配的,而不是 Java 的 new
关键字。这意味着内存直接由操作系统管理,而不是 Dalvik 管理。
本地堆和 Dalvik 堆之间唯一的真正区别是 Dalvik 堆会进行垃圾回收,而本地堆不会。
但对于这些目的,两者之间没有太大区别。当您的 Bitmap 对象被垃圾回收时,它的析构函数将在本地堆中回收相关内存。
来源:
这里有一个重要的细节:尽管位图像素在本地堆中分配,但Dalvik中的一些特殊技巧使其归入Java堆。这样做有两个原因:
(1) 控制应用程序分配内存的数量。如果没有计算,应用程序可以分配大量内存(因为位图对象本身非常小,但可以拥有任意大量的本地内存),超过16MB或24MB堆限制。
(2) 帮助确定何时进行垃圾回收。如果没有计算,您可以在100个位图对象上分配和释放引用;GC不会运行,因为这些对象很小,但它们实际上可能代表了大量兆字节的实际分配,现在无法及时进行GC。通过将这些分配计入Java堆,垃圾收集器将运行,因为它认为正在使用内存。
请注意,从许多方面来看,这是一个实现细节;未来很可能会发生变化,但这种基本行为以某种形式保持不变,因为这些都是管理位图分配的重要特性。
从实际部署情况来看,我发现以下设备:
24 MiB 通常用于高分辨率设备,并可以通过 Runtime.getRuntime().maxMemory() 进行检测。现在也有32MiB的设备,一些已经越狱的手机默认具有64MiB。以前,我曾多次因尝试弄清楚所发生的情况而迷惑不解。我认为所有设备都将位图计入堆限制中。但要对 Android 设备群体做出任何概括性的总结是极其困难的。
这是 Android 上非常麻烦且非常令人困惑的问题。这个限制及其行为文档不完整、复杂,极不直观。它们在不同设备和操作系统版本之间也会有所差异,并存在一些已知的 bug。问题的一部分在于限制并不精确 - 由于堆碎片化,你将在真正的限制之前遇到 OOM,必须保守地留下一两个 meg 的缓冲区。更糟糕的是,我有几台设备在出现 Java OOM 异常之前会发生本地 segfault(这完全是 Android 本身的 bug),这使得永远不超过限制变得更加重要,因为你甚至无法捕获本地崩溃。如需了解更多详情,请查看此帖子。在同一帖子中,我还解释了如何针对限制测量使用情况并避免崩溃。
Java 堆大小为 Runtime.getRuntime().totalMemory()。
没有简单的方法来测量本地位图存储的大小。整个本地堆可以通过 Debug.getNativeHeapAllocatedSize() 进行测量,但只有位图计入限制(我认为)。
android:largeheap="true"
来增加堆大小。这将解决你的一些问题。