使用 PLinq AsParallel 时发生内存泄漏

Memory leak in using PLinq AsParallel

我写了一段使用AsParallel并发读取大文件的测试代码。它会导致内存泄漏。 GC 似乎没有像预期的那样回收未使用的对象。请查看代码片段。

        static void Main(string[] args)
        {
            int[] array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            //foreach (var element in array) // No leaks
            //{
            //    ReadBigFile();
            //}

            array.AsParallel().ForAll(l => ReadBigFile()); // Memory leak

            Console.ReadLine();
        }

        private static void ReadBigFile()
        {
            List<byte[]> lst = new List<byte[]>();
            var file = "<BigFilePath>"; // 600 Mb
            lst.Add(File.ReadAllBytes(file));
        }

我尝试了同步和并行两种方式。由于没有内存泄漏,同步 foreach 运行正常。但是当我使用 AsParallel 并发读取文件时,内存泄漏发生了,因为它占用了 6 GB 的内存并且永远不会返回。

请帮助确定根本原因是什么?如果我想同时完成相同的任务,应该写些什么呢?谢谢。

PS: dotnet framework (4.6.1) 和 dotnet core (6.0) 都会出现这个问题。

分配给您的 600MB 文件的对象被认为是“大对象”,因此 allocated on the Large Object Heap

要清理这些对象,需要进行第 2 代收集。这不像 short-lived 对象的第 0 代集合那样频繁发生。关于垃圾收集器如何工作的复习课程是理解这一点的好主意。

GC.Collect()“释放”此内存的原因是因为不带参数调用它会执行每一代的完整收集..包括第 2 代中的大对象堆。

为了解决您对生产内存的担忧 - 如果可能,您应该考虑流式传输这些文件。如果没有,您将需要小心地批处理文件,因为并行处理数百个 half-gig 文件可能会削弱您的 CPU 和 IO,具体取决于环境。运行时只能从 OS.

请求这么多内存

@DiplomacyNotWar 我喜欢你之前的评论

If you do it sequentially then you'll have no references to the object from the previous loop, which makes it available for moving to the next generation and ultimately garbage collection. Blockquote

然后我将代码修改为

        int[] array = new[] { 1, 2 };
        for (int i = 0; i < 5; i++)
        {
            array.AsParallel().ForAll(l => ReadBigFile()); 
        }

不,我可以看到内存分配只有1.1GB,这应该是上一轮循环所需的内存大小。所以我认为现在我确信,这只是 GC 的时间问题,而不是真正的内存泄漏。非常非常感谢@DiplomacyNotWar!