我正在解析精度不是我主要考虑的数据。即使我使用最大Java堆大小,我经常会得到java.lang.OutOfMemoryError。所以我主要关心的是内存使用和java堆空间。我应该使用double或float数据类型吗?
答案 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 。
动态调整大小没有浪费。
解决方案:
ArrayList<Float>
与ArrayList<Double>
没有任何区别。这不是解决方案
要获得最大保存效果,请根据您的精度要求使用float[]
或double[]
。预分配数组以保存所需的确切元素数。
如果您希望动态调整大小的灵活性,那么第三方库可以实现节省空间的原始类型列表。或者实施自己的。但是,您无法使用标准List<...>
API,因为这会迫使您走下使用Float
或Double
的道路。
1 - 使用的实际空间取决于ArrayList
的创建和填充方式。如果您预先分配一个具有正确容量的ArrayList
,您将使用我上面提到的空间。如果通过反复追加具有默认初始容量的ArrayList
来构建阵列,则对于32位JVM,将使用平均 N * 2字节的额外空间。这是因为ArrayList
用于在后备阵列已满时生成后使用的启发式算法。
2 - 在64位JVM上,指针占用8个字节而不是4个...除非您使用压缩的oops。
3 - 它占用相同字节数的原因是在典型的JVM上,由于堆节点填充,Float
和Double
都是16个字节。
4 - 每个阵列有一个(通常)12个字节的头开销,并且数组的堆节点大小被填充为8个字节的倍数。
答案 1 :(得分:2)
如果您的内存使用量与大量(数百万)浮点数相关(可以使用合适的内存分析器进行验证),那么您最有可能将它们存储在某些数据结构(如数组或列表)中
建议(我猜你已经关注了大部分......):
float
超过double
,因为它只消耗一半大小。java.lang.Float
或java.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
)。