首页 > 解决方案 > C#:释放内存使用

问题描述

对于开箱即用的问题,我需要执行一个长期繁重的过程。所以我把这个过程分成了多个子过程。现在的问题是如何在每个窗口执行之前释放内存。

用一个例子更容易解释。让我们看一下这个伪代码。

1. Some earlier code to do other things
2. Do
3.     Raise a Task
4.     If raised-task > 1000
5.         Wait all raised task to finish
6.         Release Memory
7.     End If
8. Wile Something not relevant

有了这个想法,我开发了下一个方法,每次达到线程限制时都会执行该方法:

List<Task> LTask();
//It's not relevant, but this list is populate like
//var task = Task.Run(() => something());
//LTask.Add(task);

private void waitForAll()
{
    //Break point 1
    Task.WhenAll(LTasks).Wait();

    LTasks.Clear();
    LTasks = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();

    //Break point 2
    LTasks = new List<Task>();
}

我预计内存在某些值附近会保持不变(有一些变化)。我是说:

  1. 已达到线程限制
  2. BreakPoint 1 中使用 Visual Studio 诊断工具的内存使用快照 --> 100MB
  3. BreakPont 2 中使用 Visual Studio 诊断工具的内存使用快照 --> 100 MB。第一个问题,为什么没有减少?所有线程都完成了,我强制垃圾收集器执行。

下次达到限制并执行此代码时,如果我再次拍摄快照,内存会不断增加:200、300、...

这是诊断工具的捕获。每次到达断点 1 时拍摄奇数快照,并在断点 2 处拍摄偶数快照。

在此处输入图像描述

第二个问题,这将继续无限制地增加,直到它抛出一个Out of memory Exception

最后一个问题,有什么办法可以解决问题,释放内存?

更新 1: 经过一些测试,感谢评论,我开发了一个测试代码来深入研究它。必须涉及其他事情。请看下一段代码。内存不断增加,没有限制。

private List<Task> LTasks = new List<Task>();
private void manageThreadholdLimit()
{
    waitForAll();
    realeaseMemory();
}

private void waitForAll()
{
    Task.WhenAll(LTasks).Wait(); 
    LTasks.Clear();
    LTasks = null;  
}
private void realeaseMemory()
{   
    GC.Collect();
    GC.WaitForPendingFinalizers();

    LTasks = new List<Task>();
}
public void Main(){
    int i = 0;

    while (true)
    {
        i++;

        var task = Task.Run(() => Thread.Sleep(100));
        LTasks.Add(task);

        //Si hemos alcanzado el máximo de paralelismo, esperamos la conclusión
        if (i % 1000 == 0) manageThreadholdLimit();

    }
}

标签: c#memory-management

解决方案


GC 收集在调试中略有不同,请参阅:(John Skeet 知道所有)所以我会在发布模式下运行它时分配日志以验证所有行为。

任何解决方案都将非常依赖于您的实际代码以及是否有非托管资源被访问。

也就是说,我以前必须处理过这样的问题,并且我以前曾以两种不同的方式“解决”过它。

一种解决方案

在执行实际工作的类中,让计数器在构造函数中递增,在终结器中递减,并等待计数器低于定义的阈值,重要的是再次运行 collect 以收集最终对象。

在继续之前请仔细检查总内存消耗,否则最终可能会出现内存不足异常。

现在这实际上会稍微增加你的内存消耗。有关更多信息,请参阅

另一种解决方案

通过使用 GC.GetTotalMemory() 甚至更好的性能计数器,有一个循环等待内存消耗下降,然后等待它下降。

如果您的资源没有被收集,这最终可能根本不做任何工作。


推荐阅读