Search code examples
heap-memorystack-overflowvirtual-memorystack-memory

Stack size in relation to virtual memory


In our Operating Systems class we mentioned virtual memory as a mechanism that abstracts physical memory to a process, and that it looks something like this (per process):

virtual memory of a process

The stack grows down the memory addresses, and the heap grows up the memory addresses. The stack starting address is pretty much the maximum address of the address space (around 2^64 - 1 on a 64-bit processor).

I do understand virtual memory, the stack and the heap but I do not get why should stack size be limited to only a couple of MBs based on this picture (in practice, stacks are only a few MBs because of caching, and large stack sizes indicate a problem in the code). In theory, a stack overflow should happen only when we run out of system memory, not when we fill up 1-3MB.

My question is: is there another break line that I am missing from this picture, that is set by the compiler / linker / operating system that marks the maximum stack size?


Solution

  • Short of absolutely crazy levels of recursion, there is no real need to have a stack anywhere near the level of gigabytes.

    Since you usually know in advance what level of stacking will be enough, it's perfectly acceptable to limit the stack size and allow the heap unfettered access. A 1M stack is actually quite large if you're pushing only base types (including pointers). For example, averaging (and this is a lot) ten eight-byte parameters per call (and doubling for extra administrative stack use), that's over 6,000 stack levels you can go through without exhausting the stack.

    And keep in mind that, if your stack is allowed to grow unfettered (and this is probably a programming error as you posit), it will probably affect other processes in terms of swapping.

    To answer your specific question (about an extra "break line"): sometimes, the stack size is set by the linker when it combines all the object files into an executable. For example, I believe the GNU linkage editor ld uses (or once used) a script with the stack size specified.

    I've vaguely remember using systems (long ago)(1) where the stack size was actually hard-coded into the linker and was small enough to cause us grief. Hence we simply used the main thread to kick up another thread (without that size limitation as the stack was allocated from the heap somewhere) to do the real work. The main thread then simply waited for the other thread to finish.

    You're absolutely correct that you could just share that space between heap bottom and stack top so that they could both freely use it.


    (1) The restriction applied only to the initial stack set up by the program loader, based on information from the executable. Even now, that restriction may or may not also apply to stacks created on the fly when creating extra threads within a process.