7

The languages in question are C/C++.

My prof said to free memory on the heap when your done using it, because otherwise you can end up with memory that can't be accessed. The problem with this is you might end up with all your memory used up and you are unable to access any of it.

Why doesn't the same concept apply to the stack? I understand that you can always access the memory you used on the stack, but if you keep creating new variables, you will eventually run out of space right? So why can't you free variables on the stack to make room for new variables like you can on the heap?

I get that the compiler frees variables on the stack, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope? If not, why not?

trincot
  • 317,000
  • 35
  • 244
  • 286
dfg
  • 777
  • 1
  • 8
  • 24
  • 5
    C/C++ isn't a language. – chris Nov 02 '13 at 23:55
  • 2
    Compiler does that for you. – dnk Nov 02 '13 at 23:57
  • @dnk Yeah I get that, but thats at the end of the scope of the variable right. Doesn't it *also* free a variable on the heap at the end of its scope? If not, why not? – dfg Nov 02 '13 at 23:58
  • 1
    @dfg: There is no such thing as a variable on the heap. You can have multiple pointers to chunks of memory on the heap. – SLaks Nov 02 '13 at 23:59
  • you need to read this: http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap?rq=1 – Dill Nov 03 '13 at 00:02
  • Heap and stack are two different concepts completely. They grow in opposite directions to avoid competition for memory (generally speaking), and more importantly the stack is not for long-term storage. You literally stack things on top of it as you go through your program calling functions, when a function returns anything it allocated on the stack is popped off. – Andon M. Coleman Nov 03 '13 at 00:03
  • Stuff on the stack is meant to be temporary in the sense that it has a very clearly defined lifetime. On heap, the memory allocation is not tied to any particular scope. Otherwise, you couldn't, for instance, create a function that allocates memory for a struct and returns a pointer to it - that struct has to remain even after we return from the function, right? – kviiri Nov 03 '13 at 00:03
  • In fact I would expect a smart compiler to reuse positions in the stack if a variable is no longer going to be used in its scope. Do not know if current compilers do it, though. – SJuan76 Nov 03 '13 at 00:03
  • What happens if the variables in code in the scope the variable is in (i.e. a function) take up so much space that you run out of memory on the stack before the scope ends? In that case wouldn't you want to be able to manually take variables off the stack? – dfg Nov 03 '13 at 00:07
  • Also how can you run out of memory on a computer? An int takes up like 4 bytes, most computers have billions of bytes of memory... (excluding embedded systems) – dfg Nov 03 '13 at 00:07
  • Nope, it is always the compiler. In fact it just defines "variable a is the SP + 4, variable b is the SP + 8". There is no "allocation" as in the heap, but just "pointer arithmetic". If the program does "SP + x" and there is no such position, BOOOM! – SJuan76 Nov 03 '13 at 00:08
  • @dfg: You didn't seriously just ask how you run out of memory? :) You have a finite address space and it is used for multiple purposes beyond variable storage. You can exhaust the address space simply by mapping a filesystem. To that end, the stack also has a limit imposed by the operating system often as a user policy if we leave the realm of theory and discuss implementation. – Andon M. Coleman Nov 03 '13 at 00:13
  • @AndonM.Coleman: Given that the OP is clearly just starting to learn how to program, I doubt he understood half of your terminology here ("finite address space", "mapping a filesystem", etc.). – user541686 Nov 03 '13 at 00:20
  • @Mehrdad: I assumed since he mentioned a professor that he was a student of computer science. Since C and C++ technically do not define either **heap** or **stack** in the context of memory allocation, only static, automatic and dynamic storage (which are often attributed to the two general concepts of heap and stack), I figured he was learning C++ in conjunction with a course on computer architecture. But I see your point. – Andon M. Coleman Nov 03 '13 at 00:38

7 Answers7

8

Dynamically allocated objects ("heap objects" in colloquial language) are never variables. Thus, they can never go out of scope. They don't live inside any scope. The only way you can handle them is via a pointer that you get at the time of allocation.

(The pointer is usually assigned to a variable, but that doesn't help.)

To repeat: Variables have scope; objects don't. But many objects are variables.

And to answer the question: You can only free objects, not variables.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Just a terminology question: A class instantiation as a local variable (v.g. `MyClass localVariable;`) is not considered an object? – SJuan76 Nov 03 '13 at 00:06
  • @SJuan76: It is. Objects *can* be variables, but don't have to be. And variables *can* be objects, but don't have to be. – Kerrek SB Nov 03 '13 at 00:15
  • 3
    To be really pedantic what has scope is *names*. A variable is a named object, but it's the name that has scope rather than the object. However, for automatic variables the lifetime of the object is the same as the scope of the name, which is why it makes sense to talk about the "scope" of the variable even though properly it is the scope of the name. – Steve Jessop Nov 03 '13 at 02:29
  • @SteveJessop: Indeed, good point - variables are a proper subset of "things with names", though, so that lack of "the whole truth" should still make sense. "Things with names" (I wouldn't call them "named objects" here, just to avoid confusion) are of course an important concept and a good next topic to study (e.g. linkage). Maybe "Objects have storage, names have scope and linkage" or something to that effect. – Kerrek SB Nov 03 '13 at 10:18
6

The end of the closed "}" braces is where the stack "frees" its memory. So if I have:

{
    int a = 1;
    int b = 2;

    {
        int c = 3; // c gets "freed" at this "}" - the stack shrinks
                   // and c is no longer on the stack.
    }
}                  // a and b are "freed" from the stack at this last "}".

You can think of c as being "higher up" on the stack than "a" and "b", so c is getting popped off before them. Thus, every time you write a "}" symbol, you are effectively shrinking the stack and "freeing" data.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • What happens if the code in the brackets take up so much space that you run out of memory on the stack before the scope ends? In that case wouldn't you want to be able to manually take variables off the stack? – dfg Nov 03 '13 at 00:06
  • @dfg how would you do that if you need the variable to finish the computation? Remember that you can put `{ }` blocks wherever you want to make a local scope if you really only need variables for a specific part of the computation. – AliciaBytes Nov 03 '13 at 00:14
3

There are already nice answers but I think you might need some more clarification, so I'll try to make this a more detailed answer and also try to make it simple (if I manage to). If something isn't clear (which with me being not a native english speaker and having problems with formulating answers sometimes might be likely) just ask in the comments. Also gonna take the use the Variables vs Objects idea that Kerrek SB uses in his answer.

To make that clearer I consider Variables to be named Objects with an Object being something to store data within your program.

Variables on the stack got automatic storage duration they automatically get destroyed and reclaimed once their scope ends.

{
    std::string first_words = "Hello World!";

    // do some stuff here...

} // first_words goes out of scope and the memory gets reclaimed.

In this case first_words is a Variable (since it got its own name) which means it is also an Object.

Now what about the heap? Lets describe what you might consider being "something on the heap" as a Variable pointing to some memory location on the heap where an Object is located. Now these things got what's called dynamic storage duration.

{
    std::string * dirty = nullptr

    {

        std::string * ohh = new std::string{"I don't like this"}    // ohh is a std::string* and a Variable
                                                                    // The actual std::string is only an unnamed
                                                                    // Object on the heap.

        // do something here

        dirty = ohh; // dirty points to the same memory location as ohh does now.

    }   // ohh goes out of scope and gets destroyed since it is a Variable.
        // The actual std::string Object on the heap doesn't get destroyed

    std::cout << *dirty << std::endl;   // Will work since the std::string on the heap that dirty points to
                                        // is still there.

    delete dirty; // now the object being pointed to gets destroyed and the memory reclaimed

    dirty = nullptr; can still access dirty since it's still in its scope.

} // dirty goes out of scope and get destroyed.

As you can see objects don't adhere to scopes and you got to manually manage their memory. That's also a reason why "most" people prefer to use "wrappers" around it. See for example std::string which is a wrapper around a dynamic "String".

Now to clarify some of your questions:

  1. Why can't we destroy objects on the stack?

    Easy answer: Why would you want to?

    Detailed answer: It would be destroued by you and then destroyed again once it leaves the scope which isn't allowed. Also you should generally only have variables in your scope that you actually need for your computation and how would you destroy it if you actually need that variable to finish your computation? But if you really were to only need a variable for a small time within a computation you could just make a new smaller scope with { } so your variable gets automatically destroyed once it isn't needed anymore.

    Note: If you got a lot of variables that you only need for a small part of your computation it might be a hint that that part of the computation should be in its own function/scope.

  2. From your comments: Yeah I get that, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope?

    They don't. Objects on the heap got no scope, you can pass their address out of a function and it still persists. The pointer pointing to it can go out of scope and be destroyed but the Object on the heap still exists and you can't access it anymore (memory leak). That's also why it's called manual memory management and most people prefer wrappers around them so that it gets automatically destroyed when it isn't needed anymore. See std::string, std::vector as examples.

  3. From your comments: Also how can you run out of memory on a computer? An int takes up like 4 bytes, most computers have billions of bytes of memory... (excluding embedded systems)?

    Well, computer programs don't always just hold a few ints. Let me just answer with a little "fake" quote:

    640K [of computer memory] ought to be enough for anybody.

    But that isn't enough like we all should know. And how many memory is enough? I don't know but certainly not what we got now. There are many algorithms, problems and other stuff that need tons of memory. Just think about something like computer games. Could we make "bigger" games if we had more memory? Just think about it... You can always make something bigger with more resources so I don't think there's any limit where we can say it's enough.

Nathan
  • 4,777
  • 1
  • 28
  • 35
AliciaBytes
  • 7,300
  • 6
  • 36
  • 47
2

So why can't you free variables on the stack to make room for new variables like you can on the heap?

All information that "stack allocator" knows is ESP which is pointer to the bottom of stack.

   N: used
 N-1: used
 N-2: used
 N-3: used <- **ESP**
 N-4: free
 N-5: free
 N-6: free
 ...

That makes "stack allocation" very efficient - just decrease ESP by the size of allocation, plus it is locality/cache-friendly.

If you would allow arbitrary deallocations, of different sizes - that will turn your "stack" into "heap", with all associated additional overhead - ESP would not be enough, because you have to remember which space is deallocated and which is not:

   N: used
 N-1: free
 N-2: free
 N-3: used
 N-4: free
 N-5: used
 N-6: free
 ...

Clearly - ESP is not more enough. And you also have to deal with fragmentation problems.

I get that the compiler frees variables on the stack, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope? If not, why not?

One of the reasons is that you don't always want that - sometimes you want to return allocated data to caller of your function, that data should outlive scope where it was created.

That said, if you really need scope-based lifetime management for "heap" allocated data (and most of time it is scope-based, indeed) - it is common practice in C++ to use wrappers around such data. One of examples is std::vector:

{
    std::vector<int> x(1024); // internally allocates array of 1024 ints on heap
    // use x
    // ...
} // at the end of the scope destructor of x is called automatically,
  // which does deallocation
Evgeny Panasyuk
  • 9,076
  • 1
  • 33
  • 54
0

Read about function calls - each call pushes data and function address on the stack. Function pops data from stack and eventually pushes its result.

In general, stack is managed by OS, and yes - it can be depleted. Just try doing something like this:

int main(int argc, char **argv)
     {
     int table[1000000000];
     return 0;
     }

That should end quickly enough.

Mios
  • 247
  • 3
  • 11
  • You're right...an optimizing compiler will simply ignore the declaration and end right away. – MarkB Nov 03 '13 at 01:56
0

Local variables on the stack don't actually get freed. The registers pointing at the current stack are just moved back up and the stack "forgets" about them. And yes, you can occupy so much stack space that it overflows and the program crashes.
Variables on the heap do get freed automatically - by the operating system, when the program exits. If you do

int x;
for(x=0; x<=99999999; x++) {
  int* a = malloc(sizeof(int));
}

the value of a keeps getting overwritten and the place in the heap where a was stored is lost. This memory is NOT freed, because the program doesn't exit. This is called a "memory leak". Eventually, you will use up all the memory on the heap, and the program will crash.

Zach Stark
  • 565
  • 3
  • 8
0

The heap is managed by code: Deleting a heap allocation is done by calling the heap manager. The stack is managed by hardware. There is no manager to call.

ScottMcP-MVP
  • 10,337
  • 2
  • 15
  • 15