Java 清空一个 ArrayList 还是只创建一个新的并让旧的被垃圾收集?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18370780/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Empty an ArrayList or just create a new one and let the old one be garbage collected?
提问by Cricketer
What are the advantages and disadvantages of emptying a collection (in my case its an ArrayList) vs creating a new one (and letting the garbage collector clear the old one).
清空一个集合(在我的例子中是一个 ArrayList)与创建一个新集合(并让垃圾收集器清除旧的)的优缺点是什么?
Specifically, I have an ArrayList<Rectangle>
called list
. When a certain condition occurs, I need to empty list
and refill it with other contents. Should I call list.clear()
or just make a new ArrayList<Rectangle>
and let the old one be garbage collected? What are the pros and cons of each approach?
具体来说,我有一个ArrayList<Rectangle>
叫做list
. 当出现某种情况时,我需要清空list
并重新填充其他内容。我应该打电话list.clear()
还是只做一个新的ArrayList<Rectangle>
,让旧的被垃圾收集?每种方法的优缺点是什么?
采纳答案by dasblinkenlight
You keep the container and call clear
when you would like to reduce the load on GC: clear()
nulls out all the references inside the array, but does not make the array eligible for reclaiming by the garbage collector. This may speed up future inserts, because the array inside ArrayList
does not need to grow. This approach is especially advantageous when the data that you plan to add to the container has roughly the same size as you clearing out.
clear
当您想要减少 GC 的负载时,您保留容器并调用:clear()
将数组内的所有引用清空,但不会使数组有资格被垃圾收集器回收。这可能会加快未来的插入速度,因为里面的数组ArrayList
不需要增长。当您计划添加到容器中的数据与您清除的数据大小大致相同时,这种方法尤其有利。
In addition, you may need to use clear
when other objects hold a reference to the array that you are about to clear.
此外,clear
当其他对象持有对您即将清除的数组的引用时,您可能需要使用。
Releasing the container and creating a new one makes sense when the size of the new data may be different from what was there before. Of course you can achieve a similar effect by calling clear()
in combination with trimToSize()
.
当新数据的大小可能与之前不同时,释放容器并创建一个新容器是有意义的。当然你可以通过clear()
结合调用来达到类似的效果 trimToSize()
。
回答by Thihara
It doesn't really matter...
真的没关系...
A List.clear() implementation sets the internal array's references to null. Effectively setting the objects to be garbage collected if there are no more references.
List.clear() 实现将内部数组的引用设置为 null。如果没有更多引用,则有效地将对象设置为垃圾收集。
If your only concern is memory there's no real measurable difference in both approaches. Even operation wise the difference will be in array allocations (in resize operations) and other such operations.
如果您唯一关心的是内存,则两种方法都没有真正可衡量的差异。即使在操作上,区别也在于数组分配(在调整大小操作中)和其他此类操作。
But clearing it may be marginally better although if creating a new list is more readable I'd go with that.
但是清除它可能会稍微好一点,尽管如果创建新列表更具可读性,我会这样做。
回答by Stephen C
The advantage of recycling an ArrayList
(e.g. by calling clear
) is that you avoid the overhead of allocating a new one, and the cost of growing it ... if you didn't provide a good initialCapacity
hint.
回收一个ArrayList
(例如通过调用clear
)的优点是您可以避免分配一个新的开销,以及增加它的成本......如果你没有提供一个好的initialCapacity
提示。
The disadvantages of recycling an ArrayList
include the following:
回收的缺点ArrayList
包括以下几点:
The
clear()
method has to assignnull
to each (used) slot in theArrayList
s backing array.The
clear()
does not resize the backing array to release memory. So if you repeatedly fill and clear a list, it will end up (permanently) using enough memory to represent the largest list that it encounters. In other word, you have increased the memory footprint. You can combat that by callingtrimToSize()
, but that creates a garbage object, etcetera1.There are locality and cross-generational issues that could affect performance. When you repeatedly recycle an
ArrayList
, the object and its backing array are likely to be tenured. That means that:The list objects and the objects representing list elements are likely to be in different areas of the heap, potentially increasing TLB misses and page traffic, especially at GC time.
Assignment of (young generation) references into the (tenured) list's backing array are likely to incur write barrier overheads ... depending on the GC implementation.
该
clear()
方法必须分配null
给ArrayList
s 支持数组中的每个(已使用)插槽。在
clear()
不调整所述背衬阵列释放存储器。因此,如果您反复填充和清除列表,它将最终(永久)使用足够的内存来表示它遇到的最大列表。换句话说,您增加了内存占用。您可以通过调用来解决这个问题trimToSize()
,但这会创建一个垃圾对象,等等1。存在可能影响性能的局部性和跨代问题。当您反复回收一个 时
ArrayList
,该对象及其后备数组很可能会被永久使用。这意味着:列表对象和表示列表元素的对象可能位于堆的不同区域,这可能会增加 TLB 未命中和页面流量,尤其是在 GC 时。
将(年轻一代)引用分配到(终身)列表的后备数组中可能会产生写屏障开销……这取决于 GC 实现。
It is not possible to accurately model the performance trade-offs for a real-life application. There are just too many variables. However, the "received wisdom" is that recycling is NOT normally a good idea if you have plenty of memory2and a half-decent garbage collector.
不可能为现实生活中的应用程序准确地建模性能权衡。变量太多了。然而,“公认的智慧”是,如果你有足够的内存2和一个半体面的垃圾收集器,回收通常不是一个好主意。
It is also worth noting that a modern JVM can allocate objects very efficiently. It merely needs to update to the heap's "free" pointer and write 2 or 3 object header words. The zeroing of memory is done by the GC ... and besides the work in doing that is roughlyequivalent to the work that clear()
does to null out references in the list that is being recycled.
还值得注意的是,现代 JVM 可以非常有效地分配对象。它只需要更新堆的“空闲”指针并写入 2 或 3 个对象头字。内存的清零是由 GC 完成的……除此之外,这样做的工作大致相当于将clear()
正在回收的列表中的引用归零的工作。
1 - It would be better for performance to create a new ArrayList than to call clear() followed by trimToSize(...). With the latter you get both the garbage collection overheads AND the overheads of superfluous nulling.
1 - 创建一个新的 ArrayList 比调用 clear() 然后调用 trimToSize(...) 对性能更好。使用后者,您将获得垃圾收集开销和多余清零的开销。
2 - A copying collector is more efficient if the proportion of garbage objects to non-garbage objects is high. If you analyse the way that this kind of collector works, the costs are almost all incurred in finding and copying reachable objects. The only thing that needs to be done to garbage objects is to block-zero-write the evacuated "from" space ready for allocation of new objects.
2 - 如果垃圾对象与非垃圾对象的比例很高,则复制收集器的效率更高。如果分析这种收集器的工作方式,成本几乎都发生在查找和复制可达对象上。唯一需要对垃圾对象做的事情就是对已撤出的“from”空间进行块零写入,以准备分配新对象。
My advice would be NOT to recycle ArrayList
objects unless you have a demonstrable need to minimize the (garbage) object creation rate; e.g. because it is the only option you have for reducing (harmful) GC pauses.
我的建议是不要回收ArrayList
对象,除非您有明显的需求来最小化(垃圾)对象创建率;例如,因为它是您减少(有害的)GC 暂停的唯一选择。
All things being equal, on a modern Hotspot JVM my understanding is that you will get best performance by doing the following:
在所有条件相同的情况下,在现代 Hotspot JVM 上,我的理解是您将通过执行以下操作获得最佳性能:
- Allocating new ArrayList objects rather than recycling.
- Use accurate
initialSize
hints when you allocate the list objects. It is better to slightly overestimate than slightly underestimate.
- 分配新的 ArrayList 对象而不是回收。
initialSize
分配列表对象时使用准确的提示。稍微高估总比稍微低估好。
回答by Martin Podval
As interesting points were already written, you can think about it even one level deeper.
由于已经写了有趣的点,您可以更深入地考虑它。
I've haven't realized it than I read article about disruptor pattern, see How does LMAX's disruptor pattern work?
我在阅读有关破坏者模式的文章时还没有意识到这一点,请参阅LMAX 的破坏者模式如何工作?
It's possible to not only to reuseunderlying collection, you can reusealso entitieswithin the collection.
这是可能的,不仅要重用底层集合,你可以重复使用还实体集合中。
E.g. suppose producer and consumer use-case. Producer can fill data into the same (cyclic) array over and over again and even use same entities. Just clear properties, internal state, and fill it's own.
例如,假设生产者和消费者用例。生产者可以一遍又一遍地将数据填充到相同的(循环)数组中,甚至可以使用相同的实体。只需清除属性,内部状态,并填充它自己的。
It's one level better solution in the point of GC view. But it's obviously special case not useful for every problem.
从 GC 的角度来看,这是一个更好的解决方案。但这显然是特殊情况,并非对每个问题都有用。