0

我正在为 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但保持开销小)?

4

2 回答 2

1

您在分配数组时面临的内存不足float非常奇怪。

如果堆中可用的最大可计数内存块小于浮点数组所需的内存,则堆会增加其大小以容纳所需的内存。

当然,如果堆已经达到应用程序可用的最大值,这将失败。这意味着,您的应用程序已经用尽了堆,然后释放了大量导致内存碎片的对象,并且没有更多的堆可供分配。但是,如果是这种情况,并且假设碎片内存足以容纳浮点数组(否则您的应用程序将无法运行),那么这只是分配顺序的问题。

如果您在应用程序启动期间为浮点数组分配所需的内存,那么您就有足够的内存供它使用。然后,您只需让您的应用程序完成剩余的工作,因为计数内存已经分配。

DDMS您可以使用in Eclipse、选择您的应用程序并按下Update Heap按钮轻松检查正在分配的内存块(和空闲的内存块) 。

只是为了避免误导你,我在发布之前已经对其进行了测试,分配了几个连续的float[300000].

问候。

于 2012-12-12T20:58:57.487 回答
0

我实际上遇到了一个问题,我想为测试用例嵌入数据。由于我声明了这样的数组,当函数超过 65,535 字节的数据时,Eclipse 一直在抱怨嵌入巨大的数组。然而,这实际上是一种相当普遍的方法。

其余的进入优化。最大的问题是:进行所有这些优化是否值得?如果您对 RAM 不敏感,那么使用 1.2 兆应该没问题。如果你有一个那么大的数组,Java 也有可能会发牢骚,但你可以做一些事情,比如使用像 LinkedList 这样更高级的数据结构,或者将数组分割成更小的数组。对于静态设置的数据,如果您正在疯狂地阅读它,我觉得数组可能是一个不错的选择。

我知道您可以为整数创建 .xml 文件,因此将其存储为整数并采用诸如乘以值、读入然后除以一个值之类的策略将是另一种选择。您还可以将文本文件之类的内容放入您的资产文件夹中。只需在应用程序中执行一次,您就可以随心所欲地读/写。

至于 double vs float,我觉得在你的情况下,一个数学或科学案例,如果你能把它拉下来,double 会更安全。如果您进行任何数学运算,则使用 double 出错的可能性会更小,尤其是像乘法这样的运算。花车通常更快。我不确定 Java 是否进行 SIMD 打包,但如果确实如此,则可以将更多的浮点数打包到 SIMD 寄存器中而不是双精度数

于 2012-12-12T18:55:35.513 回答