Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# thread memory usage

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

like image 345
Robert Avatar asked Nov 21 '15 18:11

Robert


2 Answers

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.

like image 60
Jakub Lortz Avatar answered Oct 25 '22 01:10

Jakub Lortz


You are running a 64 bits application that just creates a lot of threads. First, it is important to know that running a Debug build of an application can yield different results than running a Release build because variables are artificially kept longer than needed in memory. Thus they will not be collected as soon as possible.

The Garbage Collector is highly optimized and will be triggered when:

  • The Gen0 has been fully allocated (the size of Gen0 is based on heuristics such as the rate at which your application do allocations etc);
  • The available memory is running out;
  • GC.Collect is called.

The reason the GC does not collect the memory as quickly as you thought is because there are simply no reason for the GC to collect memory. If your application would be running on a 32 bits platform, it would still have 1.5GB of memory available. In your case, you are running a 64 bits application: there is plenty memory available. However, if you were running a 'real' application with a lot more memory allocations, you would have a different outcome and probably a lot more collections (and thus a smaller working set).

Finally, calling GC.Collect is often unnecessary and can mess up the GC's heuristics which can badly impact the performance of your application because GC.Collect will trigger the collection of all the generations.

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

like image 31
Kzrystof Avatar answered Oct 25 '22 02:10

Kzrystof