假设我们从<k,v>
删除了一个条目ConcurrentHashMap
。 ConcurrentHashMap
中的删除操作将克隆必须删除的节点之前的节点。
现在我的问题是Java垃圾如何收集必须删除的节点之前的原始节点。
让我们说1
---&gt; 2
---&gt; 3
---&gt; 4
---&gt; 5
---&gt; 6
是一个由ConcurrentHashMap
维护的hashentry列表。现在我们要删除3
。
以下代码是Java ConcurrentHashMap
HashEntry newFirst = e.next;
for (HashEntry p = first; p != e; p = p.next) {
newFirst = new HashEntry(p.key, p.hash, newFirst, p.value);
tab[index]= newFirst;
}
第一次迭代后
1
---&GT; 2
---&GT; 3
---&GT; 4
---&GT; 5
- - &GT; 6
1A
---&GT; 2A
---&GT; 4
---&GT; 5
---&GT; 6
将创建一个新节点1A
,该节点将指向4
。
原始节点3
仍指向节点4
。
因此,节点4
由2个节点
第二次迭代后
1
---&GT; 2
---&GT; 3
---&GT; 4
---&GT; 5
- - &GT; 6
1A
---&GT; 2A
---&GT; 4
---&GT; 5
---&GT; 6
节点4
是指向现在两个列表的指针(1
---&gt; 2
---&gt; 3
)和({{1} } ---&GT; 1A
)。节点2A
(4
---&gt; 1
---&gt; 2
)之前的原始节点永远不会从符号列表中删除。
这是内存泄漏的情况。 GC将如何收集(3
---&gt; 1
---&gt; 2
),因为3
仍在引用它们?
答案 0 :(得分:5)
我认为你误读了那段代码。首先,循环看起来像这样:
HashEntry newFirst = e.next; // the element after the deleted one, in our case: 4
for (HashEntry p = first; p != e; p = p.next) {
newFirst = new HashEntry(p.key, p.hash, newFirst, p.value);
}
tab[index]= newFirst; // this is outside the loop
其次,这是一个单链表,所以迭代是这样的:
Step 0: tab[index] --> 1 --> 2 --> 3 --> 4 --> 5 --> 6
newFirst ----------------------^
Step 1: tab[index] --> 1 --> 2 --> 3 --> 4 --> 5 --> 6
newFirst --------------> 1A ---^
Step 2: tab[index] --> 1 --> 2 --> 3 --> 4 --> 5 --> 6
newFirst -------> 2A --> 1A ---^
Step 3: tab[index] --> 2A--> 1A--> 4 --> 5 --> 6
1 --> 2 --> 3 ----^
(步骤0是初始状态,步骤1和2是循环的两次迭代,步骤3是tab[index] = newFirst
)
正如您所看到的,在第3步之后,没有任何内容指向1
,因此它符合GC的条件,因此2
和3
也是如此。强>
P.s。:另请注意新列表中2A
和1A
的顺序是如何反转的。除非碰撞太多,否则这应该没有实际影响,但在这种情况下,碰撞本身比这引入的微小时间不一致要大得多。