3

I was reading an article on the Memory Layout of C Programs; https://www.geeksforgeeks.org/c/memory-layout-of-c-program/.

It mentioned, under "4. Stack Segment":

When the stack and heap meet, the program’s free memory is exhausted.

My questions:

  1. How is it determined what amount of size/capacity will be allocated for the stack-heap space?
  2. Say, I have an application that I know might end up requiring a lot of heap-space. Is there a way to ask for an enlarged stack-heap space?

Thank you, in advance.
Please let me know if I can better frame my questions.

3
  • 5
    The information there is old and for a particular C implementation. In current modern systems, the stack space is most often determined by a limit set when linking the program, and it is a default most people ignore most of the time. Last time I checked, the default stack sizes for a main thread were 8 MiB on macOS, 2 MiB on Linux, and 1 MiB on Microsoft Windows (each using default/common tools for that platform). You could ask for more via a linker switch, which depends on which linker you are using. Total virtual memory space will limit “heap” space, and that is subject to system policy. Commented Oct 28 at 14:55
  • 1
    The size of the stack is determined by the linker. See this answer for some examples. The size of the heap is bounded by, if nothing else, the available system memory (this is the kernel's responsibility). The size of the virtual address space through which the system memory is accessed is not typically of concern in 64-bit computing. Commented Oct 28 at 14:59
  • 2
    @XavierPedraza: It is not determined by the compiler. The stack size is generally written into an entry in the executable file, which is created by the linker. Commented Oct 28 at 15:03

1 Answer 1

8

Collisions between heap and stack are somewhere between "extraordinarily unlikely" and "physically impossible" on a modern computer with a 64-bit address space and virtual addressing. For example, on the computer I'm typing this on, the heap of an arbitrarily selected process currently ends at 0x0000_562c_9643_8000 and the next higher chunk of address space that's in use (which isn't the stack, it's a memory-mapped file) begins at 0x0000_7f8f_02e0_0000. So there's a little more than 245 bytes of unused address space (≈ 41 terabytes) that the heap could grow into. The computer only has 32 gigabytes (for ease of comparison, that's 235 bytes exactly) of physical RAM; any program that allocates an unbounded amount of heap memory will hit that limit first.

It is still easily possible to run out of stack space, though, because the stack is given a maximum size on process creation and that size is usually not very big. For the same process, the stack size limit is 8388608 bytes, or exactly 8 megabytes. However, there's roughly 443 gigabytes of unused address space in between the stack and the next lower chunk of address space that's in use (remember that hardware call stacks grow toward lower addresses); the stack limit is small not because there isn't room, but as a guard rail. Most programs don't need deep recursion, but infinite recursion is an easy bug to write, so we set the stack limit fairly small so that infinite recursion will be trapped quickly. Programs that do need deep recursion can ask the operating system for a bigger stack. On Unix, this is done with the setrlimit system call for the initial thread, and pthread_attr_setstacksize for additional threads; I don't know what the Windows equivalents are.


The previous major generation of CPUs had 32-bit address spaces with virtual addressing. 232 bytes is only 4 gigabytes, and often a quarter, or even half, of that was reserved for the OS kernel. Even so, collisions between heap and stack were rare. Typically, on those systems, the program, static data, and the beginning of the heap were loaded very close to address zero, and the stack began at the highest address that a user space process could use, leaving a gap of at least a gigabyte between them. Programs that need to allocate an entire gigabyte of heap are rare, and were even rarer back then. (The stack limit was still in the range of 8 megs, and so you did have to tweak that sometimes.)


Collisions between heap and stack are a real concern when programming for CPUs with 8- or 16-bit address spaces, and CPUs with 32-bit address spaces that don't implement virtual addressing. These are no longer used for full-sized "computers" but are still relatively common as "microcontrollers" to be "embedded" in gadgets of one stripe or another. "Retrocomputing", programming within the limits of certain 8- and 16-bit computers that were widely used in the past, is also a popular pastime. If you are interested in doing either embedded programming or retrocomputing, you will need to consult the documentation for the specific small environment you're working with, to learn how to configure the space available for the stack and the heap.


As a final note, please be advised that geeksforgeeks is notorious for inaccuracy. In this case, it appears to me that the article was written by someone who learned C programming in the days of 16-bit Windows and hasn't bothered to update their understanding since—even though the examples quote addresses that indicate they ran their test program on 64-bit Windows. Do not take anything you read on that site as definitive truth. This doesn't mean you shouldn't consult it at all, it just means you should always cross-check what you read there against some other reference. If you can afford them, I strongly recommend investing in paper copies of the textbooks Computer Systems: A Programmer's Perspective and Advanced Programming in the UNIX Environment; these are also somewhat out of date but their general accuracy level is much higher. (If you can't afford them, see if you can get them from your local public library.)

Sign up to request clarification or add additional context in comments.

3 Comments

Even though post is tagged "operating-system", most processors these days (billions per year) are little embedded ones, possibly without an OS, in your toothbrush, toaster, etc. Here C is quite popular and issues like stack and heap meeting is a real issue.
You are wrong about microcontrollers address space. Firstly, there are still 8 and 16 bit microcontrollers (AVRs as in Arduino, PICs etc). Even 32-bit microcontrollers without MMU (those on a smaller side, e.g. Cortex-M, simpler RISC-V and such) would usually have just a little bit of physically contiguous memory, and without MMU it's only that tiny speck of address space that programs could use. Running out of allocated stack and overwriting something else like heap or static variables is totally a thing still.
Thanks for letting me know. Answer has been corrected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.