缩小集合的内存开销

时间:2016-03-11 15:05:50

标签: java memory collections

我最近一直在研究Java Collections。我注意到ArrayListArrayDequeHashMap包含帮助函数,如果需要,它们可以扩展容器的容量,但如果容器变空,它们都没有功能来缩小容量。

如果我是正确的,引用的内存成本(4字节)是否无关紧要?

1 个答案:

答案 0 :(得分:4)

您是正确的,大多数集合都具有自动扩展且永不缩小的内部容量。例外是ArrayList,其中包含方法ensureCapacity()trimToSize(),可让应用程序明确管理列表的内部容量。在实践中,我相信很少使用这些方法。

增长但不自动缩减的政策是基于对集合使用模型的一些假设:

  • 应用程序通常不知道他们想要存储多少元素,因此在添加元素时,集合会自动扩展;
  • 一旦集合完全填充,元素的数量通常会保持在该数字附近,既不会增长也不会显着缩小;
  • 与元素本身的大小相比,集合的每元素开销通常较小。

对于符合这些假设的应用程序,该策略似乎运行得相当好。例如,假设您将一百万个键值对插入HashMap。默认加载因子为0.75,因此内部表大小为133万。表格大小向上舍入到下一个2的幂,即2^21(2,097,152)。从某种意义上说,这是一百万左右的"额外的"地图内部表格中的插槽。由于每个插槽通常是一个4字节的对象引用,因此浪费了4MB的空间!

但请注意,您正在使用此地图存储一百万个键值对。假设每个键和值是50个字节(这似乎是一个非常小的对象)。这是存储数据的100MB。与此相比,4MB的额外地图开销并不是那么大的交易。

但是,假设您已经存储了一百万个映射,并且您想要遍历所有映射并删除除了一百个感兴趣的映射之外的所有映射。现在您要存储10KB的数据,但是地图的2^21元素表占用了8MB的空间。这很浪费。

但似乎从地图中执行999,900次删除似乎不太可能。如果要保留100个映射,则可能需要创建一个新映射,只插入要保留的100个映射,然后丢弃原始映射。这样可以消除空间浪费,也可能会快得多。鉴于此,在实践中缺乏自动收缩政策通常不是问题。