java - 清空一个 ArrayList 还是只创建一个新的并让旧的被垃圾收集?

标签 java collections arraylist garbage-collection

清空一个集合(在我的例子中是一个 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/

相关文章:

java - 使用 Lambda 部分展平 Java 中的嵌套集合

java - 如何在 ArrayList 内的对象内创建日历对象

GNU/Linux 上的 Java 数据库连接

java - Jpa Repository save() 不更新现有数据

java - jQuery - JSON 填充下拉列表

scala - 专门集合中的 AnyVal 元素是否需要装箱?

c# - ASP.NET 在 OnPreInit 之前处理按钮单击事件

java - 如何更改 Android 飞行模式的行为,使其不会关闭蜂窝 radio ?

java - ArrayList 和 BufferedReader 笔记程序

java - 应用程序在 JVM 1.6_33 中崩溃,而在 1.7_25 中不崩溃