频繁调用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

posted @ 2020-12-28 15:46  zendwang  阅读(792)  评论(0编辑  收藏  举报