頻繁調用ConcurrentLinkedQueue類的offer和remove方法會內存泄露

頻繁調用ConcurrentLinkedQueue類的offer和remove方法會內存泄露
看一下ConcurrentLinkedQueue類的remove方法

public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
        implements Queue<E>, java.io.Serializable {
....
 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)
                  pred.casNext(p, next);
              return true;
          }
          pred = p;
      }
      return false;
  }
...
}

調用remove(object) ,當object是最後一個元素,這時候會把該元素設置爲null,該元素的next爲null,但並不會執行 pred.casNext(p, next);這時候就會導致元素並未從該隊列刪除。

該問題在jdk-8u102已解決

public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
        implements Queue<E>, java.io.Serializable {
....
    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) {
                    if (!o.equals(item)) {
                        next = succ(p);
                        continue;
                    }
                    removed = p.casItem(item, null);
                }

                next = succ(p);
                if (pred != null && next != null) // unlink
                    pred.casNext(p, next);
                if (removed)
                    return true;
            }
        }
        return false;
    }
...
}

在低於jdk-8u102版本測試

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ConcurrentLinkedQueueMemoryLeak {
 
    public static void main(String[] args)
    {
        Queue<Object> queue = new ConcurrentLinkedQueue<>();
        queue.offer(new Object());
        
        Object item = new Object();
        
        long iterations = 0;
        try
        {
            while (true)
            {
                ++iterations;
                queue.offer(item);
                queue.remove(item);
            }
        }
        catch (OutOfMemoryError e)
        { 
            queue = null;
            System.err.println("iterations: " + iterations);
            throw e;
        }
    }
}

設置運行時VM參數:-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=F:/opt/logs/clq_heapdump.hprof
設置堆內存爲1Mb大小,運行一會就報OOM
通過Java VIsualVM 打開clq_heapdump.hprof,java,util.currentLinkedQueue$Node實例數24871

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章