关于清除链表的Java最佳实践

关于清除链表的Java最佳实践,java,collections,garbage-collection,java-8,Java,Collections,Garbage Collection,Java 8,我正在用java编写一个数据结构链表(为了学习,我没有使用任何标准的java库),我想通过清空引用来清除数据结构。请告诉我哪种方法更好 1) 只需将列表的起始引用设为null,就足够了 2) 除了取消起始值,我还将所有内部节点的所有下一个指针都取消引用为null。这对垃圾收集器有帮助吗 LinkedList实现的JDK遵循了Isee方法2中的混淆,但TreeMap没有看到相同的混淆 我正在使用JDK 8这是一个有趣的问题,答案有很长的历史,有着微妙的权衡 简而言之,清除引用对于数据结构的正确操作

我正在用java编写一个数据结构链表(为了学习,我没有使用任何标准的java库),我想通过清空引用来清除数据结构。请告诉我哪种方法更好

1) 只需将列表的起始引用设为null,就足够了

2) 除了取消起始值,我还将所有内部节点的所有下一个指针都取消引用为null。这对垃圾收集器有帮助吗

LinkedList实现的JDK遵循了Isee方法2中的混淆,但TreeMap没有看到相同的混淆


我正在使用JDK 8这是一个有趣的问题,答案有很长的历史,有着微妙的权衡

简而言之,清除引用对于数据结构的正确操作不是必需的。既然您正在学习数据结构,我建议您在自己的实现中不要担心这个问题。我的直觉(尽管我还没有对此进行基准测试)是,在典型情况下,清除所有链接节点可能带来的任何好处都不会明显

(此外,在典型情况下,
LinkedList
很可能会被
ArrayList
ArrayDesk
所超越。有基准可以说明这一点。找到
LinkedList
优于其他的工作负载并不太困难,但它比其他人都少。)nk.)

当我得知
LinkedList
clear
操作将所有节点从彼此和包含的元素中取消链接时,我感到非常惊讶。下面是到的链接。此更改可追溯到2003年,并且出现在JDK 5中。此更改由bug跟踪。此更改(或稍早的更改)当单个节点与列表断开链接时,清除这些节点的下一个和上一个引用。该错误报告中还有一个测试用例,值得关注

问题似乎是可以更改
链接列表
,该列表创建垃圾节点的速度比垃圾收集器回收垃圾节点的速度快。这最终会导致JVM内存不足。垃圾节点都链接在一起的事实似乎也会妨碍垃圾收集器,使mutator线程更容易超越收集器线程(我不清楚让多个线程改变列表有多重要。在测试用例中,它们都在列表上同步,因此没有实际的并行性)更改为
LinkedList
以取消节点之间的链接,使收集器更容易执行其工作,因此显然使测试不再耗尽内存

快进到2009年,
LinkedList
代码被“整容”。bug对此进行了跟踪并在本文中进行了讨论。“整容”的最初动机之一是将
clear()
操作从O(n)减少到O(1),因为对于该bug的提交者来说,取消所有节点的链接似乎是不必要的。(对我来说,这显然是不必要的!)但是经过一些讨论后,决定取消链接行为为垃圾收集器提供了足够的好处来保留它并记录它

该评论还说,取消节点链接

确保释放内存,即使存在可访问的迭代器

这是指一种病理性病例,如下所示:

// fields in some class
List<Obj> list = createAndPopulateALinkedList();
Iterator<Object> iterator;

void someMethod() {
    iterator = list.iterator();
    // ...
    list.clear();
}
//某些类中的字段
List List=createAndPopulateLinkedList();
迭代器;
void方法(){
迭代器=list.iterator();
// ...
list.clear();
}
迭代器指向链接列表的一个节点。即使列表已被清除,迭代器仍会保持一个节点处于活动状态,并且由于该节点具有下一个和上一个引用,列表中以前的所有节点都仍然处于活动状态。取消链接
clear()中的所有节点
允许收集这些。不过,我认为这是相当病态的,因为迭代器很少存储在字段中。通常迭代器是在单个方法中创建、使用和丢弃的,大多数情况下是在单个
for
循环中创建、使用和丢弃的


现在,关于
TreeMap
。我不认为
LinkedList
取消其节点链接的根本原因是
TreeMap
没有。有人可能会认为整个JDK代码库都是一致维护的,因此如果
LinkedList
取消其节点链接是一种好的做法,那么这也应该是正确的已经对
TreeMap
进行了修改。唉,事实并非如此。很可能发生的情况是,客户在
LinkedList
中遇到了病理行为,并在那里进行了更改,但没有人在
TreeMap
中观察到类似的行为。因此,没有动力更新
TreeMap
l工作。(2)会影响实时行为,可能会花费时间。(2)的要点特别是长度为N的双链表可能需要N个垃圾步骤,但一个gc可以执行多个步骤。这取决于所使用的gc算法。但由于缺少可达性是gc的主要要求,因此我不关心这一点。实时行为更为重要。如果您认为数据结构类似于Java的LinkedList I实现,那么我认为这两种方法中的任何一种都不合适。第二种方法会更好,因为分代GC可以做得更好。
LinkedList
TreeMap
之间的主要区别在于后者与现实生活中的代码有关联。如果你关心性能,就不会使用
LinkedList
。所以当
LinkedList
浪费CPU周期来“帮助垃圾收集器”时,没关系……感谢@Stuart给出了清晰、具体和明确的答案。了解语言历史和铁饼让我感觉很棒