今天在社区看到一个有意思的JDK Bug,连接地址https://club.perfma.com/article/2041676java
问题是这样的,ConcurrentLinkedQueue在JDK7中remove方法会发生内存溢出,写代码去验证我这里就省略了eclipse
JDK7
以下List-1是JDK7中的源码spa
List-1code
public boolean remove(Object o) { if (o == null) return false; Node<E> pred = null; for (Node<E> p = first(); p != null; p = succ(p)) { E item = p.item; if (item != null && o.equals(item) && p.casItem(item, null)) { Node<E> next = succ(p); if (pred != null && next != null)//1 pred.casNext(p, next);//2 return true; } pred = p; } return false; }
来分析下是如何产生内存溢出的,以下图1,首先add(1),以后add(2),此时2的next是null,用List-1中的remove()删除2后,以下图2,该节点的value只是设置为null,可是并无将该节点从链表中删除,这是由于List-1中1处,因为链表中最后一个节点的next老是null,因此没法执行3处的代码,即释放最后一个节点对象
图1blog
图2队列
此时add(3)后,变为以下图3,以后再将3删除后,是以下图4,节点3仍是未移除,仍是和2节点未删除的缘由一致,因此能够看出这就是内存溢出的缘由ip
图3内存
图4rem
JDK8
JDK8中的remove以下List-2所示
List-2
public boolean remove(Object o) { if (o != null) { Node<E> next, pred = null; for (Node<E> p = first(); p != null; pred = p, p = next) { boolean removed = false; E item = p.item; if (item != null) {//1 if (!o.equals(item)) { next = succ(p); continue; } removed = p.casItem(item, null); } next = succ(p);//2 if (pred != null && next != null) // unlink 3 pred.casNext(p, next); if (removed) return true; } } return false; }
JDK8中add(1)和add(2)后,仍是如图1所示,以后用List-2中的remove删除2后,仍是如图2所示,即2节点未被移除链表,不要慌,看接下来
以后add(3)后,仍是如图3,此时用List-2的remove删除3后,实际上3节点未被移除队列,而是2节点被移除了队列,以下图5
图5
类分析下,在List-2的1处,因为2节点值是null,因此不会执行if中的语句,在2处将next指向3节点,以后再3处因为pred(节点1)不为null且next(节点3)不为null,因此将pred的next设置为节点3,这样节点2的next虽然连着节点3,可是因为没有对象引用节点2,因此节点2会被GC回收。