清空集合(在我的情况下是一个ArrayList)与创建一个新集合(并让垃圾收集器清除旧集合)有什么优点和缺点。
具体来说,我有ArrayList<Rectangle>
名为list
。当某种情况发生时,我需要清空list
并用其他内容重新填充。我应该拨打list.clear()
还是只创建一个新的ArrayList<Rectangle>
并让旧的垃圾收集?每种方法的优缺点是什么?
答案 0 :(得分:44)
回收ArrayList
(例如通过调用clear
)的好处是,您可以避免分配新费用的开销,以及增加费用的成本......如果您没有提供一个很好的initialCapacity
提示。
回收ArrayList
的缺点包括:
clear()
方法必须将null
分配给ArrayList
支持数组中的每个(已使用)广告位。
clear()
不会调整后备阵列的大小以释放内存。因此,如果您反复填写并清除列表,它将(永久地)使用足够的内存来表示它遇到的最大列表。换句话说,您增加了内存占用。您可以通过调用trimToSize()
来创建垃圾对象等来解决这个问题。
可能会影响性能的地方和跨代问题。当您反复回收ArrayList
时,该对象及其后备阵列可能会占用。这意味着:
列表对象和表示列表元素的对象可能位于堆的不同区域,可能会增加TLB未命中和页面流量,尤其是在GC时间。
将(年轻代)引用分配到(终身)列表的支持数组中可能会产生写屏障开销......取决于GC实现。
无法准确地为实际应用程序的性能权衡建模。变量太多了。然而,“接受的智慧”是,如果你有足够的记忆 1 和一个不太合适的垃圾收集器,回收通常不是一个好主意。
值得注意的是,现代JVM可以非常有效地分配对象。它只需要更新到堆的“空闲”指针并写入2或3个对象标题字。内存归零由GC完成......此外,这样做的工作大致等同于clear()
在清除列表中正在回收的引用时所做的工作
1 - 如果垃圾对象与非垃圾对象的比例很高,则复制收集器效率更高。如果分析这种收集器的工作方式,那么几乎所有成本都是在查找和复制可到达对象时产生的。对垃圾对象唯一需要做的就是阻止零写入撤离的“从”空间准备分配新对象。
我的建议不是回收ArrayList
个对象,除非你有明确的需要尽量减少(垃圾)对象的创建速度;例如因为它是减少(有害)GC暂停的唯一选择。
在所有条件相同的情况下,在现代Hotspot JVM上,我的理解是通过执行以下操作可以获得最佳性能:
initialSize
提示。最好稍微过高估计而不是略微低估。答案 1 :(得分:35)
保留容器并在希望减少GC负载时调用clear
:clear()
使数组中的所有引用都为空,但不会使数组有资格进行回收垃圾收集器。这可能会加快将来的插入速度,因为ArrayList
内的数组不需要增长。当您计划添加到容器的数据与清除的数据大致相同时,此方法尤其有利。
此外,当其他对象拥有您要清除的数组的引用时,可能需要使用clear
。
当新数据的大小可能与之前的数据不同时,释放容器并创建新容器是有意义的。当然,通过将clear()
与trimToSize()
结合使用,您可以获得类似的效果。
答案 2 :(得分:1)
由于已经写好了有趣的观点,你可以更深入地思考它。
我没有意识到这一点,而不是我读过关于破坏者模式的文章,请参阅How does LMAX's disruptor pattern work?
不仅可以重复使用基础集合,您还可以重复使用以及实体。
E.g。假设生产者和消费者用例。生产者可以反复将数据填充到相同的(循环)数组中,甚至可以使用相同的实体。只需清除属性,内部状态,并填充它自己。
在GC视图中,这是一个更好的解决方案。但显然特殊情况对每个问题都没有用。
答案 3 :(得分:-1)
这并不重要......
List.clear()实现将内部数组的引用设置为null。如果没有更多引用,则有效地将对象设置为垃圾回收。
如果您唯一关注的是记忆,那么这两种方法都没有真正可衡量的差异。即使是操作方面,区别在于数组分配(在调整大小操作中)和其他此类操作。
但清除它可能会略微好一些,但如果创建一个新列表更具可读性,我会选择它。