java 使用ConcurrentLinkedQueue会导致内存泄漏吗?

b0zn9rqh  于 2023-04-19  发布在  Java
关注(0)|答案(2)|浏览(372)

我从一本书上看到下面的代码会导致内存泄漏,书上说我们应该删除代码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
xggvc2p6

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 -我没有看原始源代码,看看是否泄漏可能发生在其他情况下,但我准备采取官方分析的表面价值。

bvjxkvbb

bvjxkvbb2#

是的。这将导致内存泄漏问题。

说明

  1. queue.add(new Object())ConcurrentLinkedQueue添加了一个新对象,这意味着queue将保留对该对象的引用,直到它被删除。
    1.但是在代码片段中,同一个对象通过while循环中的queue.remove(object)被重复地添加和删除。
    这可能会导致ConcurrentLinkedQueue无限期地保留对该对象的引用,即使不再需要该对象时也是如此,从而导致内存泄漏。
    要修复内存泄漏问题,请删除queue.add(new Object())行,只将对象示例添加到队列中,该队列将在while循环中使用。

注意:确保不再需要该对象后,将其从队列中删除。

相关问题