清空一个ArrayList或者只是创建一个新的ArrayList并将其旧的垃圾收集?

Cri*_*ter 38 java collections garbage-collection arraylist

清空集合(在我的情况下是一个ArrayList)与创建一个新集合(并让垃圾收集器清除旧集合)的优点和缺点是什么.

具体来说,我有一个ArrayList<Rectangle>list.当某种情况发生时,我需要清空list并用其他内容重新填充.我是应该打电话list.clear()还是只是制作一个新的ArrayList<Rectangle>并让旧的垃圾收集?每种方法的优缺点是什么?

Ste*_*n C 47

回收ArrayList(例如通过调用clear)的好处是,您可以避免分配新的开销,以及增加它的成本...如果您没有提供良好的initialCapacity提示.

回收利用的缺点ArrayList包括:

  • clear()方法必须分配nullArrayLists后备阵列中的每个(使用过的)插槽.

  • clear()不调整所述背衬阵列以释放存储器.因此,如果您反复填写并清除列表,它将(永久地)使用足够的内存来表示它遇到的最大列表.换句话说,您增加了内存占用.您可以通过调用trimToSize()...创建垃圾对象等来解决这个问题.

  • 存在可能影响性能的局部性和跨代性问题.当您反复循环使用时ArrayList,该对象及其后备阵列可能会占用.这意味着:

    • 列表对象和表示列表元素的对象可能位于堆的不同区域,可能会增加TLB未命中和页面流量,尤其是在GC时间.

    • 将(年轻代)引用分配到(终端)列表的后备阵列中可能会产生写屏障开销......取决于GC实现.


对于现实生活中的应用程序,不可能准确地建模性能权衡.变量太多了.然而,"收到的智慧"是,如果你有足够的内存1和半合适的垃圾收集器,回收通常不是一个好主意.

值得注意的是,现代JVM可以非常有效地分配对象.它只需要更新到堆的"空闲"指针并写入2或3个对象标题字.内存归零由GC完成......此外,这样做的工作大致相当于将clear()正在回收的列表中的引用置空的工作.

1 - 如果垃圾对象与非垃圾对象的比例很高,则复制收集器效率更高.如果分析这种收集器的工作方式,那么几乎所有成本都是在查找和复制可达对象时产生的.对垃圾对象唯一需要做的就是阻止零写入撤离的"从"空间准备分配新对象.


我的建议是不回收ArrayList对象,除非你有明确的需要尽量减少(垃圾)对象的创建速度; 例如,因为它是减少(有害)GC暂停的唯一选择.

在所有条件相同的情况下,在现代Hotspot JVM上,我的理解是通过执行以下操作可以获得最佳性能:

  • 分配新的ArrayList对象而不是回收.
  • initialSize分配列表对象时使用准确的提示.略微过高估计略高估.

  • 位置点是最有趣的一个,对我来说它不太明显。 (2认同)

das*_*ght 37

clear当您希望减少GC上的负载时,保留容器并调用:clear()清空数组中的所有引用,但不使该数组符合垃圾回收器的回收条件.这可能会加速未来的插入,因为内部的数组ArrayList不需要增长.当您计划添加到容器的数据与清除的大小大致相同时,此方法尤其有用.

此外,clear当其他对象持有您要清除的数组的引用时,可能需要使用它.

当新数据的大小可能与之前的大小不同时,释放容器并创建新容器是有意义的.当然,你可以通过clear()联合调用来达到类似的效果 trimToSize().