我从一本书上看到下面的代码会导致内存泄漏,书上说我们应该删除代码queue.add(new Object());
,它不会导致内存泄漏。但我不知道为什么。为什么?
import com.google.common.base.Stopwatch;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
public class ConcurrentLinkedQueueMemLeak
{
public static void main(String[] args) throws InterruptedException
{
ConcurrentLinkedQueue<Object> queue =
new ConcurrentLinkedQueue<>();
queue.add(new Object()); // ① 这一行代码会导致内存泄漏
Object object = new Object();
int loops = 0;
// 休眠10秒,方便打开JDK诊断工具,监控执行前后的内存变化
TimeUnit.SECONDS.sleep(10);
Stopwatch watch = Stopwatch.createStarted();
while (true)
{
// 每执行10000次进行一次耗时统计,并且输出
if (loops % 10000 == 0 && loops != 0)
{
long elapsedMs = watch.stop()
.elapsed(TimeUnit.MILLISECONDS);
System.out.printf("loops=%d duration=%d MS%n", loops, elapsedMs);
watch.reset().start();
}
queue.add(object);
// ② remove方法删除object
queue.remove(object);
++loops;
}
}
}
在删除代码队列之前,add(new Object()):
loops=10000 duration=588 MS
loops=20000 duration=1881 MS
loops=30000 duration=3175 MS
loops=40000 duration=3452 MS
loops=50000 duration=3784 MS
loops=60000 duration=4424 MS
loops=70000 duration=4761 MS
loops=80000 duration=5733 MS
在移除代码队列之后,添加(new Object()):
loops=363590000 duration=0 MS
loops=363600000 duration=0 MS
loops=363610000 duration=0 MS
loops=363620000 duration=0 MS
loops=363630000 duration=1 MS
2条答案
按热度按时间xggvc2p61#
这本书中提出的问题实际上是由OpenJDK Bug引起的:https://bugs.openjdk.org/browse/JDK-8054446。
感谢@MacGyver找到了这个!!
根据bug报告分析:
“Reporter是正确的-
remove(Object)
永远不会取消链接死节点 *,只要删除尾部的元素1 *。”(重点另加)
bug报告显示,该问题最初是在2014年针对Java 8提出的,并于2019年在Java 9中得到修复。该修复随后被反向移植到Java 8和Java 7。
请注意,Java 7已停产。要获得包含修复的Java 7版本,我认为您要么需要Oracle支持合同,要么需要使用第三方Java 7版本。
所以你回答你的问题:
使用
ConcurrentLinkedQueue
会导致内存泄漏吗?如果您使用的是过时的Java版本,并且您反复删除队列的最后一个元素,则选择Yes。否则选择No。
解决方案:使用最新版本的Java 7、8或更高版本。
我还应该指出,在
Queue
/Deque
上使用remove(E)
无论如何都是一个坏主意。与add(E)
,offer(E)
,remove()
,poll()
,element()
和peek()
(以及等效的Deque
操作)的O(1)
相比,它是一个O(N)
操作。1 -我没有看原始源代码,看看是否泄漏可能发生在其他情况下,但我准备采取官方分析的表面价值。
bvjxkvbb2#
是的。这将导致内存泄漏问题。
说明:
queue.add(new Object())
向ConcurrentLinkedQueue
添加了一个新对象,这意味着queue将保留对该对象的引用,直到它被删除。1.但是在代码片段中,同一个对象通过while循环中的queue.remove(object)被重复地添加和删除。
这可能会导致
ConcurrentLinkedQueue
无限期地保留对该对象的引用,即使不再需要该对象时也是如此,从而导致内存泄漏。要修复内存泄漏问题,请删除
queue.add(new Object())
行,只将对象示例添加到队列中,该队列将在while循环中使用。注意:确保不再需要该对象后,将其从队列中删除。