我正在为 Android 开发一个可视化应用程序(包括运行 Android 2.2 的旧设备)。
我的应用程序的输入模型包含一个区域,通常由数万个顶点组成。典型模型有 50000-100000 个顶点(每个顶点都有一个x、y、z float
坐标),即它们使用了 600K-1200 KB 的总内存。该应用程序要求所有顶点在内存中随时可用。这就是我可以分享的关于这个应用程序的全部内容(我不允许分享高级用例),所以我想知道我的以下结论是否正确以及是否有更好的解决方案。
例如,假设有count
=50000 个顶点。我看到两个解决方案:
1.)我早期的解决方案是使用自己的VertexObj
(由于封装更好的可读性,访问单个坐标时更好的局部性):
public static class VertexObj {
public float x, y, z;
}
VertexObj mVertices = new VertexObj[count]; // 50,000 objects
2.)我的另一个想法是使用大的float[]
代替:
float[] mVertices = new VertexObj[count * 3]; // 150,000 float values
第一个解决方案的问题是内存开销很大——我们在移动设备上,应用程序的堆可能被限制在 16-24MB(我的应用程序也需要内存来处理其他事情)。根据Android官方页面,在非真正需要时应避免对象分配。在这种情况下,即使对于 50,000 个顶点,内存开销也可能很大:
首先,“有用”的内存是 50000*3*4 = 600K(这被float
值用完了)。然后我们有 +200K 由于VertexObj
元素的开销,可能还有 +400K 由于 Java 对象头(在 Android 上每个对象也可能至少 8 个字节)。这是 50,000 个顶点的 600K“浪费”内存,这是 100% 的开销 (!)。在 100,000 个顶点的情况下,开销为 1.2MB。
第二种解决方案要好得多,因为它只需要有用的 600K 作为float
值。
显然,结论是我应该选择float[]
,但我想知道这种情况下的风险。请注意,我的怀疑也可能与内存管理的较低级别(不是严格的 Android 特定)方面有关。
据我所知,当我写入时new float[300000]
,应用程序会请求 VM 保留一个 300000*4 = 1200K 字节的连续块。(我在 Android 中遇到过,我请求了一个 1MB byte[]
,我得到了一个OutOfMemoryException
,即使 Dalvik 堆有超过 1MB 的空闲空间。我想这是因为它无法保留一个连续的 1MB 块。)
由于Android的VM的GC不是compacting GC,如果内存“碎片化”,那么这么大的float[]
分配恐怕会导致OOM。如果我在这里,那么这个风险应该被处理。例如,分配更多float[]
对象(每个对象将存储一部分,例如 200KB)怎么样?这种链表内存管理机制被操作系统和虚拟机使用,所以我觉得我需要在这里(在应用程序级别)使用它听起来很不寻常。我错过了什么?
如果没有,那么我猜最好的解决方案是使用float[]
对象的链接列表(以避免OOM但保持开销小)?