浮动或双重类型的存储和内存

时间:2017-09-21 09:37:48

标签: java memory double heap

我正在解析精度不是我主要考虑的数据。即使我使用最大Java堆大小,我经常会得到java.lang.OutOfMemoryError。所以我主要关心的是内存使用和java堆空间。我应该使用double或float数据类型吗?

2 个答案:

答案 0 :(得分:3)

  

我一直得到OOM异常,因为我使用了大量带数字的ArrayLists。

是你的问题!

  • N个32位浮点值的ArrayList在32位JVM中至少需要 1 20 * N字节,在64位中至少需要24 * N字节位JVM 2

  • N个64位浮点值的ArrayList需要相同的空间量 3

  • 以上仅说明了支持数组和列表元素。如果您有大量的小ArrayList个对象,ArrayList对象本身的开销可能很大。 (为每个ArrayList对象添加16或24个字节。)

  • 如果您使用动态调整大小,这可能会在后备阵列增长时生成对象流失。在某些时候,后备阵列的大小可能是它需要的两倍。

相比之下:

  • 32位浮点值数组大约需要4 * N字节 4

  • 64位浮点值数组大约需要8 * N字节 4

  • 动态调整大小没有浪费。

解决方案:

  1. ArrayList<Float>ArrayList<Double>没有任何区别。这不是解决方案

  2. 要获得最大保存效果,请根据您的精度要求使用float[]double[]。预分配数组以保存所需的确切元素数。

  3. 如果您希望动态调整大小的灵活性,那么第三方库可以实现节省空间的原始类型列表。或者实施自己的。但是,您无法使用标准List<...> API,因为这会迫使您走下使用FloatDouble的道路。

  4. 1 - 使用的实际空间取决于ArrayList的创建和填充方式。如果您预先分配一个具有正确容量的ArrayList,您将使用我上面提到的空间。如果通过反复追加具有默认初始容量的ArrayList来构建阵列,则对于32位JVM,将使用平均 N * 2字节的额外空间。这是因为ArrayList用于在后备阵列已满时生成后使用的启发式算法。

    2 - 在64位JVM上,指针占用8个字节而不是4个...除非您使用压缩的oops。

    3 - 它占用相同字节数的原因是在典型的JVM上,由于堆节点填充,FloatDouble都是16个字节。

    4 - 每个阵列有一个(通常)12个字节的头开销,并且数组的堆节点大小被填充为8个字节的倍数。

答案 1 :(得分:2)

如果您的内存使用量与大量(数百万)浮点数相关(可以使用合适的内存分析器进行验证),那么您最有可能将它们存储在某些数据结构(如数组或列表)中

建议(我猜你已经关注了大部分......):

  • 如果数字范围和精度足够,则首选float超过double,因为它只消耗一半大小。
  • 不要使用java.lang.Floatjava.lang.Double类进行存储,因为与裸标量值相比,它们会产生相当大的内存开销。
  • 请务必使用数组,而不是像java.util.List这样的集合,因为它们存储了盒装java.lang.Float个实例,而不是裸数。

但除此之外,有一个像样的内存分析器可以显示哪些实例占用了大部分内存。除了float / double数据之外,还有其他内存消费者。

编辑:

OP最近的评论“我一直得到OOM例外,因为我使用了大量带有数字的ArrayLists”,这一点很清楚。与ArrayList<Float>相比,float[]浪费了大量内存(Stephen C在答案中给出了详细的数字),但却带来了动态调整大小的好处。

所以,我看到了以下可能性:

  • 如果您可以从头开始告诉数组大小,请立即使用float[]数组。
  • 如果在初始化实例时需要动态大小,请在构建一个对象时使用ArrayList<Float>(当大小仍然增加时),然后将内容复制到float[]数组以进行长期存储。然后,浪费的ArrayLists仅在有限的时间内存在。
  • 如果您需要在数据的整个生命周期内使用动态尺寸,请根据代码需要的FloatArrayList数组创建自己的float[]类,类似于ArrayList<Float>(可以从非常浅的实现到功能齐全的List,也许基于AbstractList)。