Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do my threads seemingly take up less than 1MB of memory?

I'm trying to get a better grasp of the .NET threading model. I've heard and read multiple times (most recently when watching this video: AppFabric.tv - Threading with Jeff Richter) that .NET threads take up at least 1MB of memory (because they set aside 1MB for their stack). Now, I tried to write some code to demonstrate this, but I end up with results like

297 threads are using 42MB of memory
298 threads are using 43MB of memory
299 threads are using 40MB of memory
300 threads are using 40MB of memory

So, the threads are seemingly not using 1MB of memory each. I have tried to the best of my ability to reproduce the program demonstrated 5 minutes into the aforementioned video, but I don't seem to get the same result and I don't understand why. As the memory consumption seems to be falling at times, I guess threads must be exiting or be put on a back burner somewhere? Or maybe I'm not measuring memory properly?

The program used to get the results above looks like this:

class Program
{
    static void Main(string[] args)
    {
        ManualResetEvent manualReset = new ManualResetEvent(false);
        int createdThreads = 0;
        try
        {
            while (true)
            {
                Thread t = new Thread(WaitOneEvent);
                t.Start(manualReset);
                createdThreads++;
                Console.WriteLine("{0} threads are using {1}MB of memory", createdThreads, System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / (1024 * 1024));
            }
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("Out of memory at {0} threads", createdThreads);
            System.Diagnostics.Debugger.Break();
            manualReset.Set();
        }
    }

    private static void WaitOneEvent(object eventObject) {
        ((ManualResetEvent)eventObject).WaitOne();
    }
}

Any insight would be greatly appreciated.

like image 340
Rune Avatar asked Oct 20 '25 01:10

Rune


2 Answers

You might like to use VirtualMemorySize64() but PrivateMemorySize64().

MS writes here:

Each new thread or fiber receives its own stack space consisting of both reserved and initially committed memory. The reserved memory size represents the total stack allocation in virtual memory. [...] The initially committed pages do not utilize physical memory until they are referenced; [...]

As the threads in the OP's example code do nothing else but waiting to be signalled, their (stack) memory does not use physical memory and therefore will not be taken into account by PrivateMemorySize64().

like image 159
alk Avatar answered Oct 22 '25 15:10

alk


Your measurement is simply too crude to accurately trace the thread stack memory consumption. It measures the amount of virtual memory that cannot be shared with another process. The only sharable memory in a .NET process is code, the native Windows DLLs, the CLR, the jitter and any native images generated by ngen.exe (normally only the .NET framework assemblies).

Everything else is private memory. Jitted code, loader heap and the garbage collected heaps are the main chunks. Which is what is throwing it off, the garbage collector will shrink virtual memory allocation when it can.

You can get better insight in the virtual memory usage with SysInternals' VMMap utility. You'll drown in the details but it shows thread stacks as a separate category.

like image 21
Hans Passant Avatar answered Oct 22 '25 14:10

Hans Passant