大量(浮点)值的最佳数据结构

时间:2012-12-12 18:43:59

标签: java android arrays memory-management

我正在为Android开发可视化应用程序(包括运行Android 2.2的旧设备)。

我的应用程序的输入模型包含一个区域,通常由数万个顶点组成。典型模型具有50000-100000个顶点(每个顶点具有 x,y,z float坐标),即它们使用600K-1200千字节的总内存。该应用程序要求所有顶点随时都可在内存中使用。这是我可以分享的关于这个应用程序的所有内容(我不允许分享高级用例),所以我想知道我的下面的结论是否正确以及是否有更好的解决方案。

例如,假设有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值用尽)。然后由于VertexObj元素,我们有+ 200K的开销,并且由于Java对象标题可能还有另外的+ 400K(它们在Android上的每个对象可能至少有8个字节)。对于50,000个顶点,这是600K“浪费”的内存,这是100%的开销(!)。在100,000个顶点的情况下,开销为1.2MB。

第二种解决方案要好得多,因为float值仅需要600K。

显然,结论是我应该选择float[],但我想知道这种情况下的风险。请注意,我的怀疑可能与内存管理的低级(不是严格的特定于Android)方面有关。

据我所知,当我写new float[300000]时,应用程序请求VM保留一个300000 * 4 = 1200K字节的连续块。 (我在Android中发现我请求了1MB byte[],我得到了OutOfMemoryException,即使Dalvik堆还有超过1MB的空闲空间。我想这是因为它无法保留连续阻止1MB。)

由于Android的VM的GC不是压缩GC,我担心如果内存“碎片化”,这样巨大的float[]分配可能会导致OOM。如果我就在这里,那么应该处理这种风险。例如。那么分配更多float[]个对象(每个对象会存储200KB这样的部分)?操作系统和虚拟机使用这种链表内存管理机制,因此我需要在这里使用它(在应用程序级别上)。我错过了什么?

如果没有,那么我想最好的解决方案是使用float[]对象的链接列表(以避免OOM但保持开销很小)?

2 个答案:

答案 0 :(得分:1)

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

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

当然,如果堆已经达到应用程序可用的最大值,则会失败。这意味着,您的应用程序已经耗尽了堆,然后释放了大量导致内存碎片的对象,并且不再需要分配堆。但是,如果是这种情况,并假设碎片化的内存足以容纳float数组(否则你的应用程序不会运行),这只是分配顺序的问题。

如果在应用程序启动期间分配float数组所需的内存,则会有足够的内存。然后,你只需让你的应用程序完成其余的工作,因为已经分配了countigous内存。

您可以使用DDMS中的Eclipse,选择您的应用,然后按Update Heap按钮,轻松检查分配的内存块(以及免费内存块)。

为了避免误导你,我在发布之前对其进行了测试,为float[300000]分配了几个重要的记忆团队。

问候。

答案 1 :(得分:0)

我实际上遇到了一个问题,我想为测试用例嵌入数据。你将有一个非常有趣的时间嵌入庞大的数组,因为当函数超过65,535字节的数据时,由于我声明了这样的数组,Eclipse一直抱怨。然而,这实际上是一种非常常见的方法。

其余的进入优化。最大的问题是:在完成所有这些优化方面值得一试吗?如果你没有硬盘上RAM,​​那么使用1.2兆就可以了。如果你有一个庞大的数组,那么Java也有可能会发牢骚,但你可以做一些事情,比如使用像LinkedList这样的更高级的数据结构,或者将数组切割成更小的数组。对于静态设置数据,如果你像疯了一样阅读它,我觉得数组可能是一个不错的选择。

我知道你可以为整数生成.xml文件,因此存储为整数,其策略如乘以值,读入它,然后将其除以值将是另一种选择。您还可以将文本文件等内容放入资源文件夹中。只需在应用程序中执行一次,即可随意读/写。

至于双重vs浮动,我觉得在你的情况下,一个数学或科学案例,如果你可以把它拉下来,那么双倍会更安全。如果你进行任何数学计算,那么双精度错误的可能性就会降低,尤其是乘法操作。花车通常更快。我不确定Java是否进行SIMD打包,但如果确实如此,可以将更多的浮点数打包到SIMD寄存器中而不是双打