ConcurrentBag是否会导致内存泄漏?

3

可能是重复问题:
ConcurrentBag中可能存在内存泄漏?

我的应用程序存在严重的内存泄漏问题。在其中一个方法中添加到本地ConcurrentBag集合中的所有数据从未被收集。

以下是我使用它的简单代码示例:

void Main()
{
    var l = new List<int>(){1,2,3,4};
    Func(l);
    l.Clear();
    l=null;
}

void Func(List<int> list)
{
    var bag = new ConcurrentBag<int>();
    Parallel.ForEach(list, k=> bag.Add(++k));

    list.Clear();

    foreach(int i in bag) //I know, I doing it wrong.
    {
        list.Add(i);
    }
}

我希望:在“Func”方法中创建的包将被创建和处理。
我看到:包从未被处理,它持有在Parallel.ForEach中创建的所有线程以及我添加的所有数据。=(
好的,我可以使用“TryTake”从包中删除项目,当我将其添加到列表中时。但是空包仍然保存在内存中。
现在我通过使用List解决了这个问题,但是我无法睡得很好,因为我在我的内存分析器中看到了这个。抱歉我的英语=)
更新:
我更改了我的“Func”方法:
void Func(List<int> list)
{
    var bag = new ConcurrentBag<int>();
    Parallel.ForEach(list, k=> bag.Add(++k));

    list.Clear();

    int i;
    while(bag.TryTake(out i))
    {
        list.Add(i);
    }

    bag = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

然后我在VS中创建项目,编译并运行我的程序。这个实例图是由我在程序完成所有工作后10分钟内收集的内存快照,使用“.Net Memory Profiler 4.0”创建的:

http://xmages.net/storage/10/1/0/f/d/upload/4c67f305.jpg (抱歉,无法发布图片)


1
有没有任何迹象表明内存永远不会被垃圾回收?仅仅因为它存在一段时间并不意味着它是一个永久的泄漏。也许ConcurrentBag使用终结器(很可能在内部类型中)导致它需要更长的时间来收集相关的内存? - Jon Skeet
帖子已更新,添加了内存分析器报告。 - svs-t
为什么需要使用 "Gc.Collect" 和 "Gc.WaitForPendingFinalisers" 来防止内存泄漏? - rollsch
1个回答

9
这是因为并发包将项目添加到线程的本地存储中。从线程的本地存储到该包的引用是强引用,只要袋子中仍有一个项目,它就确保至少有一个线程引用该Bag,因此它不会被收集。消耗掉袋子里的所有项目或使用不同的容器。
请参阅:Possible memoryleak in ConcurrentBag?

1
帖子已更新。现在所有项目都已删除。为什么对象“ThreadLocalList”已经存在? - svs-t
我已经阅读过了,但是没有找到我的问题的答案。我知道ConcurrentBag中有几个存储。我不明白为什么它们不被GC收集,虽然所有的项目和引用都被删除了。 - svs-t
1
我一直在检查,猜测https://dev59.com/cWw05IYBdhLWcg3w6GHH可以给我们答案。 - Polity
感谢您提供有用的信息! - svs-t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接