清空一个集合(在我的例子中是一个 ArrayList)与创建一个新集合(并让垃圾收集器清除旧集合)的优缺点是什么。
具体来说,我有一个 ArrayList<Rectangle>
调用list
.当某种情况发生时,我需要清空list
并用其他内容重新填充它。我应该调用list.clear()
或者只是制作一个新的ArrayList<Rectangle>
让旧的垃圾收集?每种方法的优缺点是什么?
最佳答案
回收 ArrayList
(例如通过调用 clear
)的优点是可以避免分配新的开销以及增加它的成本...如果您没有提供良好的 initialCapacity
提示。
回收ArrayList
的缺点包括:
clear()
方法必须将null
分配给ArrayList
的后备数组中的每个(已使用)槽。clear()
不会调整后备数组的大小以释放内存。因此,如果您反复填充和清除列表,它将最终(永久)使用足够的内存来表示它遇到的最大列表。换句话说,您增加了内存占用。你可以通过调用trimToSize()
来解决这个问题,但这会创建一个垃圾对象,等等1。存在可能影响性能的地方性和跨代问题。当您反复回收
ArrayList
时,对象及其支持数组可能会被永久使用。这意味着:列表对象和表示列表元素的对象可能位于堆的不同区域,可能会增加 TLB 未命中和页面流量,尤其是在 GC 时。
将(年轻一代)引用分配到(永久)列表的后备数组中可能会产生写屏障开销...取决于 GC 实现。
不可能准确地为实际应用程序的性能权衡建模。变量太多了。然而,“公认的智慧”是,如果你有足够的内存2 和一个半体面的垃圾收集器,回收通常不是一个好主意。
同样值得注意的是,现代 JVM 可以非常有效地分配对象。它只需要更新堆的“空闲”指针并写入 2 或 3 个对象头字。内存归零是由 GC 完成的......除了这样做的工作大致等同于 clear()
所做的工作以清空正在回收的列表。
1 - 创建一个新的 ArrayList 比调用 clear() 后跟 trimToSize(...) 更好。使用后者,您将获得垃圾收集开销和多余空值的开销。
2 - 如果垃圾对象与非垃圾对象的比例很高,则复制收集器的效率更高。如果分析这种收集器的工作方式,成本几乎都发生在查找和复制可达对象上。对垃圾对象唯一需要做的就是将撤离的“来自”空间的 block 零写入准备好分配新对象。
我的建议是不要回收 ArrayList
对象,除非您有明显的需要最小化(垃圾)对象创建率;例如因为它是减少(有害的)GC 暂停的唯一选择。
在所有条件相同的情况下,在现代 Hotspot JVM 上,我的理解是通过执行以下操作可以获得最佳性能:
- 分配新的 ArrayList 对象而不是回收。
- 在分配列表对象时使用准确的
initialSize
提示。稍微高估总比低估好。
关于java - 清空一个 ArrayList 还是只创建一个新的并让旧的被垃圾收集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18370780/