删除并发散列映射

时间:2015-07-09 09:24:07

标签: java memory-leaks concurrenthashmap

假设我们从<k,v>删除了一个条目ConcurrentHashMapConcurrentHashMap中的删除操作将克隆必须删除的节点之前的节点。

现在我的问题是Java垃圾如何收集必须删除的节点之前的原始节点。

让我们说1 ---&gt; 2 ---&gt; 3 ---&gt; 4 ---&gt; 5 ---&gt; 6是一个由ConcurrentHashMap维护的hashentry列表。现在我们要删除3

以下代码是Java ConcurrentHashMap

中remove方法的代码段
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)。节点2A4 ---&gt; 1 ---&gt; 2)之前的原始节点永远不会从符号列表中删除。

    < / LI>

这是内存泄漏的情况。 GC将如何收集(3 ---&gt; 1 ---&gt; 2),因为3仍在引用它们?

1 个答案:

答案 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的条件,因此23也是如此。

P.s。:另请注意新列表中2A1A的顺序是如何反转的。除非碰撞太多,否则这应该没有实际影响,但在这种情况下,碰撞本身比这引入的微小时间不一致要大得多。