頻繁調用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