c#线程内存使用情况

时间:2015-11-21 18:10:31

标签: c# multithreading

我正在学习更多关于线程的知识,我使用以下代码创建了一个相当简单的WPF应用程序(x64平台构建)

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        for (var i = 0; i <= 20000; i++)
        {
            Thread thread = new Thread(Test);
            thread.IsBackground = true;

            thread.Start();
        }
    }

    public void Test()
    {
        Thread.Sleep(20000);
    }
}

当我运行此代码时,进程占用大约560MB的RAM,而所有线程都在运行/休眠。

Highest peak

完成后,进程使用率下降到大约125 MB RAM。

我的问题是,为什么当应用程序本身(没有线程示例)仅使用30 MB的RAM时,进程使用125 MB的RAM?

它是否会保留一些线程以供可能重新使用或正在进行其他操作?

修改

由于一些建议如何改进此代码,我想指出的是,我并没有想办法改进它,而是要找出这种行为的原因。

编辑2:

这不是线程相关的,但我在内存中尝试了一个包含大string列表的案例,并且它没有产生相同的结果。当列表在内存中完全加载时,它占用了大约1.3 GB的内存,但是在列表设置为NULL并且调用GC.Collect()之后,内存使用率按预期下降回到30 MB。

代码:

public partial class MainWindow : Window
{
    List<string> stringArray = new List<string>();

    public MainWindow()
    {
        InitializeComponent();


        for (var i = 0; i <= 100000000; i++)
        {
            //Thread thread = new Thread(Test);
            //thread.IsBackground = false;

            //thread.Start();

            stringArray.Add("Some very long string to pump up the memory volume 2 reloaded");
        }

        stringArray = null;
        GC.Collect();
    }



}

Lowest peak

3 个答案:

答案 0 :(得分:7)

线程完成时,每个线程使用的部分内存将被释放。这就是当所有后台线程完成时,内存消耗从560 MB降至125 MB的原因。

其余内存在托管堆上分配,将由垃圾收集器释放。

当您的应用程序处于空闲状态时,不会分配新的内存,因此不需要运行垃圾收集器。您在评论中提到使用GC.Collect()强制GC没有帮助,但请记住Thread已经实现了终结器,因此它将在第一次收集后继续存在。你必须使用

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

确保释放内存。

这应该在启动线程之前将内存使用量减少到该级别。

答案 1 :(得分:2)

您正在运行一个只创建大量线程的64位应用程序。首先,重要的是要知道运行应用程序的Debug构建可以产生与运行Release构建不同的结果,因为变量被人为地保持比内存中所需的更长。因此,他们不会尽快收集。

垃圾收集器经过高度优化,将在以下情况下触发:

  • Gen0已完全分配(Gen0的大小基于启发式,例如您的应用程序执行分配的速率等);
  • 可用内存不足;
  • 调用GC.Collect。

GC没有像您想象的那样快速收集内存的原因是因为GC根本没有理由收集内存。如果您的应用程序将在32位平台上运行,它仍然有1.5GB的可用内存。在您的情况下,您正在运行64位应用程序:有足够的内存可用。但是,如果你正在运行一个真正的&#39;在具有更多内存分配的应用程序中,您将获得不同的结果,并且可能会有更多的集合(因此工作集更小)。

最后,调用GC.Collect通常是不必要的,并且可能会破坏GC的启发式,这会严重影响应用程序的性能,因为GC.Collect将触发所有代的集合。

https://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx

答案 2 :(得分:0)

您的线程机制是问题的一部分。除了它在执行过程中占用了大量的CPU时间(减少了垃圾收集的机会)之外,还有你的内存使用情况。

这样的东西(减去调试信息)将使你的内存消耗非常均匀。而你的CPU应该是“懒散”的。在操作期间。(static关键字来自我在控制台应用程序中的测试)。 .Net框架4.0或更高版本是必需的。如果您受限于.net的早期版本,那么在您开始新任务之前我会建议稍微停顿一下,以便垃圾收集能够实现它的魔力。

private static void ThreadStuff()
{
    long startSet = System.Diagnostics.Process.GetCurrentProcess().WorkingSet64;
    List<long> endSet = new List<long>();
    for (var i = 0; i <= 20000; i++)
    {
        Action Act = new Action(() => Test(i));

        Task Tsk = new Task(Act);
        Tsk.Start();
        endSet.Add(System.Diagnostics.Process.GetCurrentProcess().WorkingSet64);
        int worker;
        int ioCompletion;
        ThreadPool.GetMaxThreads(out worker, out ioCompletion);
        Console.WriteLine(worker);
        Console.WriteLine(ioCompletion);
    }
    Console.WriteLine(startSet.ToString("###,###,###,###,###,###.####"));
    Console.WriteLine(endSet.Average().ToString("###,###,###,###,###,###.####"));
}
public static void Test(int Index)
{
    Thread.Sleep(2000);
    Console.WriteLine(Index.ToString() + " Done");
}