如何释放Parallel.Task使用的内存?

我有一个程序进行内存密集型模拟。 下面我写了一个小型控制台应用程序,它复制了我遇到的问题。

class Program { static void Main(string[] args) { var t = new Task(() => DoMemoryHog(20000000)); t.Start(); t.Wait(); t.Dispose(); t = null; GC.Collect(); Console.WriteLine("Done"); Console.ReadLine(); } static void DoMemoryHog(int n) { ConcurrentBag results = new ConcurrentBag(); Parallel.For(0, n, (i) => { results.Add(Math.Sqrt(i.GetHashCode())); }); } } 

当我运行程序时,我可以看到Windows任务管理器中已用内存量的增加,但是当任务完成(并显示“完成”)时,内存不会恢复到原始级别,只会发生当我关闭应用程序时。

有没有人知道如何释放并行任务使用的内存,而主应用程序一直在运行? 正如你所看到的,我已经尝试过处理它,将它的引用设置为null并手动运行垃圾收集器(我知道你不应该这样做)。

我确定你的内存没有问题,.NET只是不会缩小已使用的内存,以便将来可以使用它。 这为将来的内存分配节省了时间。 尝试在完成后重新运行循环,我确定内存不会增长。

所以请试试这个,我会对结果感兴趣!

 class Program { static void Main(string[] args) { Process currentProcess = Process.GetCurrentProcess(); for (int i = 0; i < 10; i++) { var t = new Task(() => DoMemoryHog(20000000)); t.Start(); t.Wait(); t.Dispose(); t = null; GC.Collect(); Console.WriteLine("Done" +i); Console.WriteLine("Memory: " + GC.GetTotalMemory(false)); Console.WriteLine("Paged: " + currentProcess.PagedMemorySize64); Console.WriteLine("-------------------------"); } Console.ReadLine(); } static void DoMemoryHog(int n) { ConcurrentBag results = new ConcurrentBag(); Parallel.For(0, n, (i) => { results.Add(Math.Sqrt(i.GetHashCode())); }); } } 

这两种方法用于打印出内存使用情况:

 GC.GetTotalMemory(); currentProcess.PagedMemorySize64; 

我的输出是:

 Done0 Memory: 480080992 Paged: 520753152 ------------------------- Done1 Memory: 480081512 Paged: 520753152 ------------------------- Done2 Memory: 480082160 Paged: 520753152 ------------------------- Done3 Memory: 480083476 Paged: 520753152 ------------------------- Done4 Memory: 480083496 Paged: 520753152 ------------------------- Done5 Memory: 480083516 Paged: 520753152 ------------------------- Done6 Memory: 480083536 Paged: 520753152 ------------------------- Done7 Memory: 480084204 Paged: 520753152 ------------------------- Done8 Memory: 480084204 Paged: 520753152 ------------------------- Done9 Memory: 480085500 Paged: 520753152 ------------------------- 

据我所知,这里没有记忆问题。 对象是清理的,内存可以重复使用。 问题解决了吗?

更新

专用字节行为:

PrivateBytes的申请

正如您所看到的,GC正在收集对象并释放内存,但它当前没有释放它,因此可以分配新对象。

这是由于concurrentBag的性质(请参阅我之前关于ConcurrentBag的问题(ConcurrentBag中可能的memoryleak? ))。

基本上,并发包将项目存储在本地线程上。 如果您不使用这些项目,这些项目将保留在本地线程上。 请检查以下示例:

  class TrackedItem { public TrackedItem() { Console.WriteLine("Constructor!"); } ~TrackedItem() { Console.WriteLine("Destructor!"); } } static void Main(string[] args) { Action allCollect = () => { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); }; // create a single item and loose it directly thereafter TrackedItem item = new TrackedItem(); item = null; allCollect(); // Constructor!, Destructor! ConcurrentBag bag = new ConcurrentBag(); bag.Add(new TrackedItem()); bag = null; allCollect(); // Constructor! // Note that the destructor was not called since there is still a collection on the local thread Console.ReadLine(); } 

concurrentBag使用ThreadLocal类,这样可以方便地为每个线程保存1个实例。 在ThreadLocal中处理数据的预期方法是在ThreadLocal上调用Dispose方法(ThreadLocal实现IDisposable)。 这仅处理当前线程的数据。 尽管如此,concurrentBag并没有处理它的ThreadLocals。 相反,它依赖于所有正在使用的项目 – 或托管ThreadLocal的线程。 但是当你在ThreadPool中共享线程时,这可能非常讨厌。