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
相關文章
相關標籤/搜索