删除并发hashmap中的条目时发生内存泄漏

che*_*rma 2 java memory-leaks concurrenthashmap

让我们说我们<k,v>从中删除一个条目ConcurrentHashMap.remove操作ConcurrentHashMap将克隆必须删除的节点之前的节点.

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

让我们说1---> 2---> 3---> 4---> 5---> 6是一个由其维护的hashentry列表ConcurrentHashMap.现在我们要删除3.

以下代码是Java中remove方法的代码片段 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;
}
Run Code Online (Sandbox Code Playgroud)
  • 第一次迭代后

    1---> 2---> 3---> 4---> 5--->6

    1A---> 2A---> 4---> 5--->6

    1A将创建一个指向的新节点4.原始节点3仍指向节点4.因此,节点4由2个节点指向

  • 第二次迭代后

    1---> 2---> 3---> 4---> 5--->6

    1A---> 2A---> 4---> 5--->6

    该节点4是一个指向两个列表的指针(1---> 2---> 3)和(1A---> 2A).Node 4(1---> 2---> 3)之前的原始节点永远不会从hashenlist列表中删除.

这不是内存泄漏的情况.GC将如何收集(1---> 2---> 3),因为它们仍被引用ConcurrentHashMap

biz*_*lop 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
Run Code Online (Sandbox Code Playgroud)

其次,这是一个单链表,所以迭代如下:

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 ----^
Run Code Online (Sandbox Code Playgroud)

(步骤0是初始状态,步骤1和2是循环的两次迭代,步骤3是tab[index] = newFirst)

正如您所看到的,在第3步之后没有任何指向1,因此它符合GC的条件,因此,23.

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