DCSIMG
A Thread’s Stack - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

A Thread’s Stack

When creating threads, we don’t usually think of its stack size. In the native world, the CreateThread function accepts a stack size (second argument) which we usually pass as 0. In the managed world, the Thread class exposes a pair of constructors expecting a stack size argument (which I was reminded by a comment).

Why is this important? Creating threads has its costs. This is not only the added work the Windows scheduler must undertake or the data structures that must be allocated in the kernel to manage that thread (KTHREAD, ETHREAD, etc.). Even if the threads are mostly waiting, memory for their stacks is wasted.

When a thread is created, actually two stacks are created: one in user space (lower addresses) and one in kernel (system) space. The latter is very limited in size (12KB in x86, 24KB in x64) and almost always resides in RAM (the reason has to do with interrupt service routines and other high IRQL code, such as DPCs, that are beyond the scope of this post). This stack size cannot be changed in any documented way, and in any case, only relevant for device driver programmers. We’ll concern ourselves with the user mode stack.

The Native World

When the stack size is specified as 0 in CreateThread, a default value, embedded in the PE header is used, which is 1MB by default. However, that 1MB is not actually committed in its entirety, but only a single page (4KB) and the next page in memory is marked with the PAGE_GUARD protection attribute, that causes an exception to be generated when the stack tries to expand beyond that first page. Windows’ memory manager responds by automatically committing the next page and moving the guard page to the following page (technically all downwards, as Intel stacks grow down in addresses, not up). So, what’s the meaning of that 1MB? This is the reserved size – that is, the maximum contiguous memory that thread stack can have. Trying to grow beyond that causes a stack overflow exception. (in this brief explanation, I’ve omitted some minor details for clarity)

Reserving memory is considered an inexpensive operation – no memory is committed, no RAM wasted, not even page file space; the only thing happening is the addition of a Virtual Address Descriptor (VAD) to indicate the fact that another address region needs to be described and marked “reserved”. But – and this is a big but – address space range is wasted. That means, other allocations (of any kind) have less address space to work with. This is mostly problematic for 32 bit processes, as they are limited to 2GB or 3GB (and sometimes 4GB on 64 bit system, look at my post for more details). 64 bit processes are mostly unaffected, as their address space is vast (~8TB).

Here’s a simple experiment we can try: how many threads can we create in a 32 bit process with 2GB user address space?

 

DWORD WINAPI DoSleep(PVOID) {

   Sleep(INFINITE);

   return 0;

}

 

int _tmain(int argc, _TCHAR* argv[]) {

   int count = 0;

   DWORD id;

   do {

      HANDLE h = CreateThread(0, 0, DoSleep, 0, 0, &id);

      if(h == NULL)

         break;

      count++;

   } while(true);

   printf("Total threads: %d\n", count);

   getchar();

 

   return 0;

}

Running this on my system yields:

image

When opening Task Manager and looking at the process memory we find:

image

The red rectangle indicates the committed size (process-wide). This is definitely less than 1456 * 1MB! But the address space is pretty full (almost 1.5GB just for stack threads!)

Most applications do not require such a large stack (1MB), so we can change that. One way is to change the size globally using a linker option. This will set a different stack size for all threads that specify 0 for the second argument to CreateThread. Here’s the dialog in Visual Studio 2008:

image

The “Stack Reserve Size” is the relevant option. Let’s change this to 65536 (64KB):

image

And run it again. This time the result is:

image 

Better than our previous 1456.

The issue is (of course) not the number of threads, as both numbers are ridiculously large. But the saving of address space allows more allocations of a “conventional” nature (malloc, new, VirtualAlloc, HeapAlloc, etc.).

The second argument to CreateThread allows changing the committed or the reserved size of that particular thread’s stack, overriding the default set by the linker option. By default, the change is in the initial committed size. To change the reserved size, one must specify the STACK_SIZE_PARAM_IS_A_RESERVATION constant (which I find to be a ridiculous and inconsistent name) in the flags argument (one before last).

What is you have a prebuilt EXE with no source code, and you suspect it’s creating too many threads with large stacks? You can use the editbin.exe tool (installed with Visual Studio) to manipulate PE header values, including this one. To do the same (reduce stack reservation to 64KB, for example) one could execute from a command prompt:

editbin /stack:65536 ThreadStack.Exe

The Managed World

In .NET, the thread’s stack size cannot be set in any visible way, and it’s set to 1MB by default. The additional problem with .NET, is that 1MB is immediately committed! (not just reserved). This means it consumes memory right away, even if the stacks don’t need to grow to 1MB.

Here’s a test to verify this:

static void Main(string[] args) {

   int count = 1;

   do {

      Thread t = new Thread(() => Thread.Sleep(Timeout.Infinite));

      try {

         t.IsBackground = true;

         t.Start();

      }

      catch(OutOfMemoryException e) {

         break;

      }

      catch(ThreadStartException e) {

         break;

      }

      count++;

   } while(true);

   Console.WriteLine("Threads: {0}", count);

   Console.ReadLine();

}

The result of running this simple app is:

image

Close to its native counterpart.

Looking at task manager reveals the big difference:

image

Note the almost 1.5GB of committed memory!

Can we change this behaviour? Not in any way I could find.

Can we at least change the default 1MB? The VS property pages do not expose this option. However, using editbin.exe works on .NET executables as well as native ones, because both are PE files with the same basic header. This works even with signed assemblies, because this value lies in the part that is not hashed by the signing process.

Using this command line:

editbin /stack:131072 ManagedThreadStack.Exe

yields:

image

Definitely an improvement!

Conclusion

Thread stack sizes need to be taken into account, especially in heavily multithreaded applications. The CLR imposes limits on what we can do, but hopefully more control will be available in future versions of the CLR.

Comments List

# A Thread's Stack – Pavel's Blog » Process Less

Published at Wednesday, September 16, 2009 7:12 PM by A Thread's Stack – Pavel's Blog » Process Less  

Pingback from  A Thread's Stack – Pavel's Blog »  Process Less

# re: A Thread’s Stack

Published at Wednesday, September 16, 2009 7:59 PM by Liran Chen  

While committing the whole stack may have its downsides, this is an absolute must for managed applications. If the memory is not committed right away, there's a chance that you'll run out of physical space during stack growth (when Windows will try to commit more memory). Obviously, this will cause a stack overflow, but at this point the CLR's vectored exception handler will only have the guard region’s worth of stack space, which doesn't leave it with much to do. Thus, the whole stack memory is committed at the thread's creation, elimination the chance that you'll run out of physical space.

Duffy discussed about this issue some time ago:

www.bluebytesoftware.com/.../TheCLRCommitsTheWholeStack.aspx

# re: A Thread’s Stack

Published at Wednesday, September 16, 2009 8:28 PM by pavely  

That may be true, but I my personal opinion is that it's a too costly price to pay, especially considering that you don't have control over the stack size.

# re: A Thread’s Stack

Published at Wednesday, September 16, 2009 8:49 PM by Liran Chen  

This is incorrect. The Thread class exposes a constructor that allows you to determine the maximum stack size for  the thread (it's been around since v2.0)

msdn.microsoft.com/.../5cykbwz4.aspx

# re: A Thread’s Stack

Published at Wednesday, September 16, 2009 9:24 PM by pavely  

You're right... somehow I forgot about that constructor(s). I guess too many uses with the "simple" constructor...

# A Thread???s Stack « Jasper Blog

Published at Thursday, September 17, 2009 10:10 AM by A Thread???s Stack « Jasper Blog  

Pingback from  A Thread???s Stack « Jasper Blog

# re: A Thread’s Stack

Published at Tuesday, September 22, 2009 4:14 PM by Solar Panel  

Hey, wonderful post. I just found your blog and I am already a fan. 8D

# re: A Thread’s Stack

Published at Monday, February 22, 2010 2:41 PM by Liran Chen  

As it turns out, as of v3.5 you may choose not to commit the entire stack by adding the <disableCommitThreadStack> element in your app.config file (207.46.16.248/.../bb882564.aspx).

Also, in case you are using CLR hosting, you may use the STARTUP_DISABLE_COMMITTHREADSTACK flag when you initialize the CLR (msdn.microsoft.com/.../ms231027%28VS.100%29.aspx).

# re: A Thread’s Stack

Published at Tuesday, August 02, 2011 2:11 AM by Cuffiffushcag  

Do u know whats the Hardest Riddle in the world ?

# re: A Thread’s Stack

Published at Wednesday, August 31, 2011 8:15 PM by Gegedeksbew  

Hey there

# re: A Thread’s Stack

Published at Thursday, September 15, 2011 6:28 AM by infaboyw  

how many time i do not do what i want to do but do what i dont want to do

# re: A Thread’s Stack

Published at Wednesday, November 09, 2011 8:58 PM by NoahEsteban  

What's up all members, I am a newly registered poster on this here board therefore I believe I ought to tell you a bit about myself. Here goes, I am 29 years old, female, and I enjoy reading physics at my uni. I definitely anticipate eagerly conversing with all you older members... Arrivederci for now!

# re: A Thread’s Stack

Published at Tuesday, November 15, 2011 10:59 AM by Urgessott  

Urgessott, latenightwithjimmysintern.com - buying xanax online  - Let your doctor know if you have depression or have had it in the pass. latenightwithjimmysintern.com - buy alprazolam

# re: A Thread’s Stack

Published at Monday, November 21, 2011 1:41 AM by Trearenny  

Trearenny, http://duckbushduck.com/ - kamagra online kaufen Die meisten dieser Medikamente besitzen unter anderem Kopfweh, Ubelkeit und andere als Nebenwirkungen, weshalb sie nur unter Vorsicht und naturlich unter arztlicher Aufsicht einzunehmen sind. http://duckbushduck.com/ - cialis online bestellen

# re: A Thread’s Stack

Published at Wednesday, January 11, 2012 6:18 PM by julietapplinger  

דומה סימבלטה  

can cymbalta cause fatty liver

cymbalta ssi

# re: A Thread’s Stack

Published at Saturday, January 14, 2012 9:57 PM by julietapplinger  

סימבלטה לשחרר המורחבת    

  how to whin off of cymbalta

   http://cymbalta-too.webs.com/

- cymbalta for neuorpathy

use of cymbalta for headaches

how to ease discontinuation symptoms cymbalta

# re: A Thread’s Stack

Published at Monday, April 30, 2012 4:43 PM by joyloogame  

Stop hack the program!!!

# re: A Thread’s Stack

Published at Sunday, May 13, 2012 10:25 PM by NadiaBlaydes71  

Discovered an helpful offer while browsing the web these days called "Smart Shoppers Saving Club ".

"The Discount Savings Club can offer you thousands of dollars in savings on dining and groceries! Tons of other ways to save include discounts on travel, movies, attractions, and much more! Submit a valid credit card for the 5 day trial offer ($1.20 cost to user).

Once you activate your Smart Shoppers Saving Club trial membership for only $1.20 activation fee billed today, you can immediately take advantage of all the exciting savings. After your 5-day trial period you will only be charged a ONE time membership fee $94.00 and then nothing more. "

<a href=alturl.com/5o4he>Smart Shoppers Saving Club</a>

What is your opinion about the Smart Shoppers Saving Club ? Is it actually worth the money?

# re: A Thread’s Stack

Published at Tuesday, May 15, 2012 7:57 PM by Swearryadvage  

buycheapgenericpropeciaonline.com buy cheap propecia no prescription

# re: A Thread’s Stack

Published at Tuesday, May 15, 2012 8:03 PM by Swearryadvage  

buycheapgenericpropeciaonline.com propecia over the counter

# re: A Thread’s Stack

Published at Tuesday, May 15, 2012 8:10 PM by Swearryadvage  

buycheapgenericpropeciaonline.com cheapest place to buy propecia

# re: A Thread’s Stack

Published at Tuesday, May 15, 2012 10:32 PM by IcotFlessycet  

buycheapgenericpropeciaonline.com buy propecia generic finasteride

# re: A Thread’s Stack

Published at Tuesday, May 15, 2012 10:38 PM by IcotFlessycet  

# re: A Thread’s Stack

Published at Wednesday, May 23, 2012 10:48 AM by vasanton  

# re: A Thread’s Stack

Published at Friday, May 25, 2012 8:39 AM by Rowgongeinfit  

Music As Medicine ,   No prescription is needed! Worldwide delivery.  No prescription is needed! Worldwide delivery. http://www.costofcipro.com/ - buy cipro online  There may be other conditions that Cipro is used to treat that are not listed here. If your doctor has prescribed Cipro be sure to let them know if you have any history of kidney or liver disease.  Cipro has also been used to treat patients that have been exposed to Anthrax which is supposed to prevent or slow down the disease.

Leave a Comment

(required) 
(
required
)
 
(optional)
(required) 

Enter the numbers above: