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