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

huangapple go评论73阅读模式
英文:

Will the use of ConcurrentLinkedQueue cause memory leak?

问题

以下是您要翻译的内容:

在一本书中,我读到以下代码会导致内存泄漏,并且书中告诉我们应该删除代码queue.add(new Object());,这样就不会导致内存泄漏。但我不知道为什么。为什么?

在删除代码queue.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

在删除代码queue.add(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
英文:

I read from a book that the following code will cause memory leak,and the book tells that we should remove the code queue.add(new Object()); and it will not cause memory leak. but I don't know why. Why?

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;
    }
}
}

before remove the code queue.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

after remove the code queue.add(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

答案1

得分: 2

The problem presented in that the book is actually caused by an OpenJDK Bug: https://bugs.openjdk.org/browse/JDK-8054446.

Kudos to @MacGyver for finding this!!

According to the analysis on the bug report:

"Reporter is correct - remove(Object) will never unlink dead Nodes if only ever the element at the tail is removed1."

(Emphasis added)

The bug report shows that the issue was initially raised against Java 8 in 2014, and was fixed in Java 9 in 2019. The fix was subsequently backported to Java 8 and Java 7.

Note that Java 7 is EOL. To get a Java 7 release with the fix, I think you will either need an Oracle support contract, or you will need to use a 3rd-party Java 7 release.

So to you answer your question:

Will the use of ConcurrentLinkedQueue cause memory leak?

If you are using an out-of-date version of Java AND you are repeatedly removing the last element of the queue, then Yes. Otherwise No.

Solution: use an up-to-date version of Java 7, 8 or later.

I should also point out that using remove(E) on a Queue / Deque is kind of a bad idea anyway. It is an O(N) operation compared with O(1) for add(E), offer(E), remove(), poll(), element() and peek() (and the equivalent Deque operations).

1 - I didn't look at the original source code to see if the leak could occur in other circumstances, but I am prepared to take the official analysis at face value.

英文:

The problem presented in that the book is actually caused by an OpenJDK Bug: https://bugs.openjdk.org/browse/JDK-8054446.

Kudos to @MacGyver for finding this!!

According to the analysis on the bug report:

> "Reporter is correct - remove(Object) will never unlink dead Nodes if only ever the element at the tail is removed<sup>1</sup>."

(Emphasis added)

The bug report shows that the issue was initially raised against Java 8 in 2014, and was fixed in Java 9 in 2019. The fix was subsequently backported to Java 8 and Java 7.

Note that Java 7 is EOL. To get a Java 7 release with the fix, I think you will either need an Oracle support contract, or you will need to use a 3rd-party Java 7 release.


So to you answer your question:

> Will the use of ConcurrentLinkedQueue cause memory leak?

If you are using an out-of-date version of Java AND you are repeatedly removing the last element of the queue, then Yes. Otherwise No.

Solution: use an up-to-date version of Java 7, 8 or later.

I should also point out that using remove(E) on a Queue / Deque is kind of a bad idea anyway. It is an O(N) operation compared with O(1) for add(E), offer(E), remove(), poll(), element() and peek() (and the equivalent Deque operations).


<sup>1 - I didn't look at the original source code to see if the leak could occur in other circumstances, but I am prepared to take the official analysis at face value.</sup>

答案2

得分: -1

Yes. This will cause memory leak issue.

Explanation:

  1. queue.add(new Object()) adds a new object to the ConcurrentLinkedQueue, which means queue will hold the reference to that object until it is removed.
  2. But in the code snippet same object is getting added and removed repeatedly, via queue.remove(object) in the while loop.

This might cause the ConcurrentLinkedQueue to keep the reference to the object indefinitely, even when the object is no longer needed, causing memory leak.

To fix the memory leak issue, remove the line queue.add(new Object()) and only add the object instance to the queue, which is being used in the while loop.

Note: Make sure that the object is removed from the queue after it is no longer needed.

英文:

Yes. This will cause memory leak issue.

Explanation :

  1. queue.add(new Object()) adds a new object to the ConcurrentLinkedQueue, which means queue will hold the reference to that object until it is removed.
  2. But in the code snippet same object is getting added and removed
    repeatedly, via queue.remove(object) in the while loop.

This might cause the ConcurrentLinkedQueue to keep the reference to the object indefinitely, even when the object is no longer needed, causing memory leak.

> To fix the memory leak issue, remove the line queue.add(new
&gt; Object())
and only add the object instance to the queue, which is
> being used in the while loop.

Note : Make sure that the object is removed from the queue after it is no longer needed.

huangapple
  • 本文由 发表于 2023年4月13日 14:49:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76002435.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定