JDK之ConcurrentLinkedQueue的Bug

    今天在社区看到一个有意思的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回收。

Reference

  1. Jetty中报出的这个Bug, https://bugs.eclipse.org/bugs/show_bug.cgi?id=477817                                               
  2. https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8137185
相关文章
相关标签/搜索