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