Java 收缩集合的内存开销
我最近一直在研究Java集合。我注意到,Java 收缩集合的内存开销,java,memory,collections,Java,Memory,Collections,我最近一直在研究Java集合。我注意到,ArrayList、ArrayDeque或HashMap包含辅助函数,这些函数可以在必要时扩展容器的容量,但它们都没有在容器变空时缩小上限的函数 如果我是对的,那么引用(4字节)的内存成本是否如此无关?你是对的,大多数集合都有一个内部容量,可以自动扩展,而且不会缩小。例外情况是ArrayList,它有方法ensureCapacity()和trimToSize(),允许应用程序显式管理列表的内部容量。在实践中,我相信这些方法很少使用 自动增长但不自动收缩的策
ArrayList
、ArrayDeque
或HashMap
包含辅助函数,这些函数可以在必要时扩展容器的容量,但它们都没有在容器变空时缩小上限的函数
如果我是对的,那么引用(4字节)的内存成本是否如此无关?你是对的,大多数集合都有一个内部容量,可以自动扩展,而且不会缩小。例外情况是
ArrayList
,它有方法ensureCapacity()
和trimToSize()
,允许应用程序显式管理列表的内部容量。在实践中,我相信这些方法很少使用
自动增长但不自动收缩的策略基于对集合使用模型的一些假设:
- 应用程序通常不知道要存储多少元素,因此集合会随着元素的添加而自动扩展李>
- 一旦集合完全填充,元素的数量通常将保持在该数量附近,既不会显著增加也不会显著减少李>
- 与元素本身的大小相比,集合的每元素开销通常较小
HashMap
中。默认负载系数为0.75,因此内部表大小为133万。表格大小被四舍五入到二的下一次幂,即2^21
(2097152)。从某种意义上说,这在地图的内部表中大约有一百万个“额外”插槽。由于每个插槽通常是一个4字节的对象引用,因此浪费了4MB的空间
但请考虑,您使用这个映射来存储一百万个键值对。假设每个键和值都是50字节(看起来像一个非常小的对象)。存储数据需要100MB。相比之下,4MB额外的地图开销并不是什么大不了的事
但是,假设您已经存储了一百万个映射,并且您希望遍历它们并删除除一百个感兴趣的映射之外的所有映射。现在您正在存储10KB的数据,但地图的2^21
元素表占用了8MB的空间。那是很大的浪费
但似乎从地图上执行999900次删除也是一件不太可能的事情。如果要保留100个映射,可能需要创建一个新映射,只插入要保留的100个映射,然后丢弃原始映射。这将消除空间浪费,而且可能会快得多。有鉴于此,在实践中,缺少集合的自动收缩策略通常不是问题。差不多。如果一个集合增长太多,需要扩展,它很可能会再次扩展。存储和性能之间的平衡。
ArrayList
具有trimToSize()
缩小“参考成本”上限是不相关的;只要是这样。要点是:如果您的程序必须对大量数据进行计算;那么它可能不再无关紧要了。示例:当您被要求处理数百万个数字时;那么,选择int数组可能是有意义的;而不是使用整数对象的ArrayList。