7

I want to understand exactly where the global variables are stored in my program. On the stack? On the heap? Somewhere else?

So for that I wrote this small code:

int global_vector[1000000];
int main () {
    global_vector[0] = 1; // just to avoid a compilation warning
    while(true); // to give me time to check the amount of RAM used by my program
    return 0;
}

No matter how large I make global_vector, the program only uses a really tiny amount of RAM. I do not understand the reason for this. Could someone please explain?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 5
    I doubt this is relevant since it's a static array, but did you try `memset`ting the entire array to zero? – Mysticial Aug 27 '12 at 20:18
  • 2
    Actually this is completely relevant. Using `memset` the program went from using 92K to 381.6M. –  Aug 27 '12 at 20:22
  • In that case, templatetypedef's answer is correct. – Mysticial Aug 27 '12 at 20:23
  • @Mysticial I find it very odd that you would doubt that the obvious is irrelevant ... what would "static array" have to do with it? Memory gets paged no matter where it is. It's important for C programmers to have a basic understanding of how modern operating systems affect the behavior of their programs. – Jim Balter Aug 27 '12 at 20:40
  • @JimBalter I was under the impression that static variables are (by default) pre-initialized to something implementation defined value. In that case, the OS is forced to commit all that memory at the start of the program. But I guess I was wrong. (I'm not much of a language pendant.) – Mysticial Aug 27 '12 at 20:42
  • 2
    @Mysticial They are pre-initialized to 0 -- that's not implementation dependent. And guess what value the OS fills newly allocated pages with? So no, the OS doesn't have to commit any memory (and this design is quite intentional). This is not a matter of languages, or "pendants' or even pedantry, it is basic knowledge about the computing environment that every programmer (certainly every C programmer) should know. – Jim Balter Aug 27 '12 at 20:46
  • 1
    @JimBalter Ha! That comes straight out of [my answer here](http://stackoverflow.com/questions/8029584/why-does-malloc-initialize-the-values-to-0-in-gcc). I didn't see that connection! :) – Mysticial Aug 27 '12 at 20:48
  • 1
    @Mysticial Like I said, "no matter where it is". So your understanding has been broadened today. :-) – Jim Balter Aug 27 '12 at 20:51
  • 1
    @Mysticial P.S. Your reference in that answer to R..'s comment touches on the same issue ... the fact that pages are zeroed can be used for efficiency ... either by calloc (for the heap), or by the loader (for "bss" memory). – Jim Balter Aug 27 '12 at 20:58

3 Answers3

11

This is completely implementation-dependent, but typically global variables are stored in a special memory segment that is separate from the stack and the heap. This memory could be allocated as a fixed-size buffer inside of the executable itself, or in a segment that is given to the program at startup by the operating system.

The reason that you're not seeing the memory usage go up probably has to do with how virtual memory is handled by the OS. As an optimization, the operating system won't actually give any memory to the program for that giant array unless you actually use it. Try changing your program to for-loop over the entire contents of the array and see if that causes the RAM usage to go up. (It's also possible that the optimizer in your compiler is eliminating the giant array, since it's almost completely unused. Putting a loop to read/write all the values might also force the compiler to keep it).

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Exactly. After the suggestion of Mysticial, I used `memset` to set all array values to zero and that made the RAM memory usage jump dramatically. –  Aug 27 '12 at 20:26
  • The fact that a **global** array is "almost" unused is irrelevant ... the *compiler* can't normally do global optimizations. There's an exception here because it's `main` and it returns before using the array, so no code in other compilation units could use it, but doing such an optimization is pointless. – Jim Balter Aug 27 '12 at 20:44
5

The optimizer is probably removing the array entirely since you never use it.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    But I did. What about the first line after `int main ()`? –  Aug 27 '12 at 20:18
  • @curvature: You only used one element in it. Never underestimate the power of the optimizer. (note that I haven't actually checked) – SLaks Aug 27 '12 at 20:19
  • 2
    @curvature What you did has no observable effect, so the compiler could remove it. – ollb Aug 27 '12 at 20:20
  • 1
    The compiler would have to be clever enough to know that no C code (which could access global_vector) will run after main returns. C compilers don't contain that particular cleverness because there's no point to it. So this is the wrong answer. – Jim Balter Aug 27 '12 at 20:34
  • You can verify whether it is being optimized out by looking at the symbol sizes in your executable. For example: running `objdump --syms myprogram.exe | grep global_vector`. If the size is large, then it is page fault explanation from other answers. If size is small, then it is compiler optimization. – TJD Aug 27 '12 at 21:39
4

Global variables that are not given explicit initializers, like yours in this case, are initialized to 0's by default. They are placed into an area of memory called the .bss segment, and no additional data is stored in the object file/executable file indicating the initial value of the data (unlike explicitly initialized data, which has to have its initial value stored somewhere).

When the OS loads the program, it reads in the descriptions of all of the segments and allocates memory for that. Since it knows that the .bss segment is initialized to all 0's, it can do a sneaky trick to avoid having to actually allocate tons of memory and then initialize it to all 0's: it allocates address space for the segment in the process's page table, but all of the pages point to the same page, filled with 0's.

That single zero-page is also set to read-only. Then, if and when the process writes to some data in the .bss segment, a page fault occurs. The OS intercepts the page fault, figures out what's going on, and then actually allocates unique memory for that page of data. It then restarts the instruction, and the code continues on its merry way as if the memory had been allocated all along.

So, the end result is that if you have a zero-initialized global variable or array, each page-sized chunk of data (typically 4 KB) that never gets written to will never actually have memory allocated for it.

Note: I'm being a little fuzzy here with the word "allocated". If you dig into this sort of thing, you're likely to encounter words such as "reserved" and "committed". See this question and this page for more info on those terms in the context of Windows.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589