我正在学习更多关于线程的知识,我使用以下代码创建了一个相当简单的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,而所有线程都在运行/休眠。
完成后,进程使用率下降到大约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();
}
}
答案 0 :(得分:7)
线程完成时,每个线程使用的部分内存将被释放。这就是当所有后台线程完成时,内存消耗从560 MB降至125 MB的原因。
其余内存在托管堆上分配,将由垃圾收集器释放。
当您的应用程序处于空闲状态时,不会分配新的内存,因此不需要运行垃圾收集器。您在评论中提到使用GC.Collect()
强制GC没有帮助,但请记住Thread
已经实现了终结器,因此它将在第一次收集后继续存在。你必须使用
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
确保释放内存。
这应该在启动线程之前将内存使用量减少到该级别。
答案 1 :(得分:2)
您正在运行一个只创建大量线程的64位应用程序。首先,重要的是要知道运行应用程序的Debug构建可以产生与运行Release构建不同的结果,因为变量被人为地保持比内存中所需的更长。因此,他们不会尽快收集。
垃圾收集器经过高度优化,将在以下情况下触发:
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");
}