Memory layout of a C program, why heap is also there

152 Views Asked by At

My own imaginary memory layout is like this:

High Addresses                                        ---------------------------
----------------                                      | A shared "heap" that is |
|    Stack     |    ptr_var points to somewhere       | transparently managed   |
|              |    inside a common shared heap       | by the OS and the only  |
|  *ptr_var    | -----------------------------------> | limit is the computer's |
|              |                                      | available memory. When  |
----------------                                      | needed, my prog. calls  |
| [Free space] |                                      | malloc(), OS does       |
----------------                                      | bookkeeping and hands   |                               
| Static Data  |                                      | over the first addr. of |   
----------------                                      | a chunk of memory from  |
|    Code      |                                      | the "heap"              |
----------------                                      ---------------------------  
 Low Adresses

But in reality, every one uses the below to represent the memory layout (ICYW: the difference between the above and below illustrations is the the above one does not have a Heap section in the program's memory space):

High Addresses
----------------
|    Stack     |
----------------
| [Free space] |
----------------
|     Heap     | --> Key different from the 1st diagram: there is a Heap section!
----------------
| Static Data  |
----------------
|    Code      |
----------------
 Low Adresses

My confusion is about the Heap section. Given the 2nd diagram, does it mean that the size of heap of the program is also limited by the memory layout?

The usual idea is that, malloc() only returns NULL when the OS does not have enough memory to hand over to my program. But if the layout is exactly like the 2nd diagram, how is it possible for the program to malloc() a lot of memory? (Say on a computer with 16GB available memory, I malloc() 15.9GB of them successfully). What will be the size of the Heap section?

High Addresses
--------------------------
|    Stack               |
| ptr1* = malloc(15.9GB) | ------------┑
--------------------------             |
|    [Free space]        |             |
--------------------------             | --> ptr1 points to some address in it's own Heap?
|                        |             |
|                        | <-----------┘
|    Heap                | 
|                        | ----------------> What is the size of Heap section? >=15.9GB?
|                        | 
--------------------------
| Static Data            |
--------------------------
|    Code                |
--------------------------
 Low Adresses
1

There are 1 best solutions below

5
On

… the only limit is the computer's available memory…

That is not the limit. A general-purpose multi-user computer system uses virtual memory. With virtual memory, each process has its own address space: It uses addresses that are, essentially, numbers in a designed address space. Each time an instruction is executed that accesses memory, the computer processor translates the virtual memory address to a physical memory address.

This means the program must have physical memory for a virtual address at the moment an instruction using the address is executed. At other times, the virtual address does not have to be mapped to actual physical memory.

The operating system manages physical memory and gives the processor information about which virtual addresses in which processes are mapped to which physical addresses. When the processor executes an instruction that uses memory, it uses that mapping information to figure out the physical address. If no physical memory is mapped for the requested virtual address, the processor stops execution of the process and informs the operating system about this. Then the operating system maps physical memory for the virtual address and tells the processor to restart the process. (This may involve unmapping other physical memory or making other changes, and, if the operating system cannot satisfy the request, it may terminate the process.)

To make physical memory available for a process, the operating system might choose some physical memory that is already in use and write its contents to a disk. Later, when the contents of that memory are needed again, the operating system will read it back into physical memory. This is called swapping. Also, some parts of memory might have originated as read-only data already stored in files on disk, such as parts of executable files. In this case, the operating system does not need to write the contents of memory to disk to save it, because it is already on disk. It merely needs to know that it can read it back later from the original file.

In this way, by swapping data in and out of memory, the operating system can make a process work even if the process uses more virtual memory than the amount of physical memory the system has.

Given the 2nd diagram, does it mean that the size of heap of the program is also limited by the memory layout?

The amount of virtual memory a process can have is limited by the virtual address space. If you are designing the layout of data in the virtual address space and you designate certain regions for stack and certain regions for static data, then you reduce the amount of memory available for other purposes because those regions are not available for other purposes.

One might put the static data at the “bottom” of the address space (at addresses starting near zero) and the stack at the “top” of the address space (near high-value addresses), leaving the middle of the address space for dynamically allocated memory. With today’s large address spaces, that leaves plenty of room for dynamically allocated memory.

Additionally, memory use is complicated by dynamically loading libraries and multithreading, which can bring new static data into a process after it has already started executing and can create new stacks. However, there is no theoretical reason why these various types of data in a process cannot be interleaved. When a process wants to request new memory mappings for static data, stack space, or dynamically allocated memory, it can ask the operating system for a block of memory, and the operating system can fit it into the address space anywhere there is an unused segment. (I am not familiar with all the details of today’s various program loaders and arrangements for managing address space, so I do not assert that any particular systems do or do not do this.)