ConcurrentBag 实现中是否存在内存泄漏?

可能重复:
ConcurrentBag中可能的内存泄漏?

EDIT1:

实际的问题是。 你能证实这一点,或者我的样本是错误的,我错过了一些明显的东西吗?

我认为ConcurrentBag是一个简单的替代无序列表。 但是我错了。 ConcurrentBag确实将自己作为ThreadLocal添加到创建线程,这基本上会导致内存泄漏。

class Program { static void Main(string[] args) { var start = GC.GetTotalMemory(true); new Program().Start(args); Console.WriteLine("Diff: {0:N0} bytes", GC.GetTotalMemory(true) - start); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Thread.Sleep(5000); } private void Start(string[] args) { for (int i = 0; i < 1000; i++) { var bag = new ConcurrentBag(); bag.Add(1); byte by; while (bag.TryTake(out by)) ; } } 

我可以制作Diff 250 KB或100 GB,具体取决于我添加到包中的数据量。 数据和包包都消失了。

当我用Windbg打破这个并且我做了一个!DumpHeap -type Concurrent

….

 000007ff00046858 1 24 System.Threading.ThreadLocal`1+GenericHolder`3[[System.Collections.Concurrent.ConcurrentBag`1+ThreadLocalList[[System.Byte, mscorlib]], System],[System.Threading.ThreadLocal`1+C0[[System.Collections.Concurrent.ConcurrentBag`1+ThreadLocalList[[System.Byte, mscorlib]], System]], mscorlib],[System.Threading.ThreadLocal`1+C0[[System.Collections.Concurrent.ConcurrentBag`1+ThreadLocalList[[System.Byte, mscorlib]], System]], mscorlib],[System.Threading.ThreadLocal`1+C0[[System.Collections.Concurrent.ConcurrentBag`1+ThreadLocalList[[System.Byte, mscorlib]], System]], mscorlib]] 000007feed812648 2 64 System.Collections.Concurrent.ConcurrentStack`1[[System.Int32, mscorlib]] 000007feece41528 1 112 System.Collections.Concurrent.CDSCollectionETWBCLProvider 000007ff000469e0 1000 32000 System.Threading.ThreadLocal`1+Boxed[[System.Collections.Concurrent.ConcurrentBag`1+ThreadLocalList[[System.Byte, mscorlib]], System]] 000007feed815900 1000 32000 System.Collections.Concurrent.ConcurrentStack`1+Node[[System.Int32, mscorlib]] 000007ff00045530 1000 72000 System.Collections.Concurrent.ConcurrentBag`1+ThreadLocalList[[System.Byte, mscorlib]] 

当我创建一个空的ConcurrentBag让一些工作线程向它添加数据时ConcurrentBag及其数据将在那里,只要创建线程仍然存活。

这样我就得到了几GB的内存泄漏。 我通过使用List和锁来“修复”这个问题。 ConcurrentBag可能很快但是对于具有相同对象生存期的List的简单替换它是无用的。

如果我在主线程上创建一个ConcurrentBag,只要线程处于活动状态,我就会保留它。 这不是我所期望的,它可能会导致重大痛苦。

你是对的,ConcurrentBag创建了一个ThreadLocal副本,实际上它们针对同一个线程正在读取数据并将数据写入包的场景进行了优化:“… ConcurrentBag是一个线程安全的包实现,针对相同的场景进行了优化线程将生成和消费存储在包中的数据。“

另一方面,我在这里看不到奇怪的行为; 线程生活和并发包生活。 当线程完成时,GC将完成它的工作。

从文档中

ConcurrentBag是一个线程安全的包实现,针对同一个线程将生成和消耗存储在包中的数据的情况进行了优化。

从何时使用线程安全的集合

在混合的生产者 – 消费者场景中,对于大型和小型工作负载,ConcurrentBag通常比任何其他并发集合类型更快且更具可伸缩性。

我会说你对ConcurrentBag的假设是不正确的。 首先,它不会将itsels添加到ThreadLocal,它使用线程本地存储为访问它的每个线程提供单独的内部列表。 它不仅仅是一个线程安全的无序列表。

一旦您意识到包使用TLS,您认为内存泄漏实际上是预期的行为 – 只要线程正在使用,就不需要清除数据。

说了这么多,直到现在我还没有意识到ConcurrentBag的额外function。

我已经找到了一个非常好的描述,ConcurrentBag如何在“ 什么是ConcurrentBag ”中的不同场景中使用单独的列表及其方法的成本。 我希望这个描述出现在MSDN文档中。

就个人而言,我现在开始使用ConcurrentBag,因为我知道它的特殊行为。

更新:

刚刚查看了Ayende的这篇文章 ,说“ThreadLocal,ConcurrentBag使用的,没想到会有很多实例。这已经修复了,现在可以运行得相当快”

为什么不在第二个GC.Collect()之后移动Console.WriteLine? 否则你可能会看到比你想象的更多的物体。

您也可以尝试将Main中的所有内容放入循环中以获取一些统计信息。 即使你不动你的写作,你也可能会看到更小的增量。

干杯!