C# thread memory usage

cmultithreading

I was learning more about threading, and i created a rather simple WPF application with the following code (x64 platform build)

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);
    }
}

When i run this code, process takes approximately 560MB of RAM while all threads are running / sleeping.

Highest peak

Upon it's completion, process usage is down to aprox 125 MB of RAM.

My question is, why does process use 125 MB of RAM at that point, when application itself (without thread example) is using only 30 MB of RAM?

Does it keep some of the threads alive for possible re/usage or something else is going on?

EDIT:

Due to some suggestions how to improve this code, I would like to point out that I am not asking a way to improve it, but to pinpoint the reason for this behavior.

EDIT 2:

This is not a thread related, but I have tried a case with a large string list in memory, and it did not produce same results. When list was fully loaded in memory, it took about 1.3 GB of memory, but after list was set to NULL, and GC.Collect() was called, memory usage dropped back to 30 MB as expected.

Code:

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

Best Answer

Part of the memory used by each thread is deallocated when the thread completes. That's why the memory consumption drops from 560 MB to 125 MB when all background threads complete.

The rest of the memory is allocated on the managed heap and will be freed by garbage collector.

When your application sits idle, no new memory is allocated, so there is no need for the garbage collector to run. You mentioned in a comment that forcing GC using GC.Collect() didn't help, but keep in mind that Thread has a finalizer implemented, so it will survive the first collection. You will have to use

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

to make sure the memory is freed.

That should reduce the memory usage to the level before starting the threads.