Java 垃圾收集器与垃圾收集

Java 垃圾收集器与垃圾收集,java,garbage-collection,Java,Garbage Collection,我读过几篇关于Java中垃圾收集的文章,但我仍然不能确定明确清除垃圾收集是否被认为是一种好的做法。。。由于我找不到一个明确的答案,我决定在这里提问 考虑这个例子: List<String> list = new LinkedList<>(); // here we use the list, perhaps adding hundreds of items in it... // ...and now the work is done, the list is not n

我读过几篇关于Java中垃圾收集的文章,但我仍然不能确定明确清除垃圾收集是否被认为是一种好的做法。。。由于我找不到一个明确的答案,我决定在这里提问

考虑这个例子:

List<String> list = new LinkedList<>();
// here we use the list, perhaps adding hundreds of items in it...
// ...and now the work is done, the list is not needed anymore
list.clear();
list = null;
List List=newlinkedlist();
//这里我们使用列表,可能会在其中添加数百项。。。
//…现在工作已经完成,不再需要列表
list.clear();
列表=空;
从我在
LinkedList
HashSet
等实现中看到的情况来看,
clear()
方法基本上只是循环给定集合中的所有项,将其所有元素(在
LinkedList
的情况下,还引用下一个和上一个元素)设置为
null

如果我做对了,将
列表
设置为
null
只会从
列表
中删除一个引用-考虑到它是对它的唯一引用,垃圾收集器最终会处理它。我只是不知道在这种情况下,列表的元素也被垃圾收集器处理需要多长时间

所以我的问题是-上面列出的示例代码的最后两行是否真的帮助垃圾收集器更高效地工作(即更早地收集列表的元素),还是让我的应用程序忙于“不相关的任务”?

我不相信clear()在这种情况下会有帮助。一旦不再有对项目的引用,GC就会删除这些项目,因此理论上,只需将list=null设置也会有相同的效果。 您无法控制何时调用GC,因此在我看来,除非您有特定的资源/性能需求,否则不值得担心。就个人而言,我仍然认为list=null


如果要重用list变量,那么当然clear()是最好的选择,而不是创建新的list对象。

最后两行没有帮助

  • 一旦
    list
    变量超出范围*,如果这是对链表的最后一次引用,那么该列表就有资格进行垃圾收集。将
    list
    立即设置为
    null
    不会增加任何值

  • 一旦列表符合垃圾收集的条件,如果列表中只包含对元素的引用,那么就可以对其元素进行垃圾收集。清除列表是不必要的

在大多数情况下,您可以信任垃圾收集器完成其工作,而不需要“帮助”它

*迂腐地说,控制垃圾收集的不是范围,而是可达性。可达性不容易用一句话来概括。有关此区别的解释,请参见


此规则的一个常见例外是,如果代码保留引用的时间比需要的时间长。这方面的典型例子是侦听器。如果您向某个组件添加了一个监听器,并且以后不再需要该监听器,则需要显式删除它。如果您不这样做,该侦听器可以禁止对其自身和它所引用的对象进行垃圾收集

假设我在按钮中添加了一个侦听器,如下所示:

button.addListener(event -> label.setText("clicked!"));
随后,标签被移除,但按钮仍保留

window.removeChild(label);
这是一个问题,因为按钮引用了侦听器,而侦听器引用了标签。即使标签在屏幕上不再可见,也不能对其进行垃圾收集

这是一个采取行动,站在GC好的一边的时候。当我添加它时,我需要记住侦听器

Listener listener = event -> label.setText("clicked!");
button.addListener(listener);
…这样我可以在处理完标签后将其删除:

window.removeChild(label);
button.removeListener(listener);

在Java中,对象要么是活动的(可通过其他对象拥有的引用访问),要么是死的(任何其他对象都无法通过引用所有者访问)。只能从死对象访问的对象也被视为死对象,并符合垃圾收集的条件

如果没有活动对象引用您的集合,那么它是不可访问的,并且符合垃圾收集的条件。这也意味着集合的所有元素(以及它可能创建的任何其他辅助对象)也不可访问,除非其他活动对象对它们有引用


因此,clear方法除了删除从一个死对象到另一个死对象的引用之外没有任何效果。无论哪种方式,他们都会被收集到垃圾。

这取决于以下因素

  • 如何实现
    clear()
  • 集合持有的条目的分配模式
  • 垃圾收集器
  • 集合或其子视图中是否存在其他内容(不适用于您的示例,但在现实世界中很常见)
对于一个原始的、不分代的跟踪垃圾收集器来说,清除引用只意味着额外的工作,而不会使GC上的事情变得更容易。但是,如果您不能保证及时取消对集合的所有引用,那么清除仍然会有所帮助

对于分代GC,尤其是G1GC,在某些情况下,通过减少跨区域引用,清除集合(或引用数组)内的引用可能会有所帮助

但这只有在您实际拥有在不同区域创建对象并将其放入另一个区域中的集合的分配模式时才会有所帮助。它还取决于将这些引用置零的
clear()
实现,这会将清除变成O(n)操作,而通常可以将其实现为O(1)操作

因此,对于您的具体示例,答案如下:

如果

  • 你的名单是长期存在的
  • 在该代码路径上创建的列表构成/保留了应用程序产生的垃圾的很大一部分
  • 您正在使用G1或类似的多代收集器
  • 在最终释放之前,缓慢累积物体(t
    /**
     * Removes all of the elements from this list.
     * The list will be empty after this call returns.
     */
    public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }