Stack vs Heap
Stack | Heap |
---|---|
Stored in RAM | Stored in RAM |
Has limited size | The size is dynamic and can be increased |
There is 1 stack for each Thread | There is 1 heap for the Application(usually) |
Stack is reclaimed when a Thread exits | Heap is reclaimed when an App exits |
Stack is faster, because an allocation and deallocation is simply a pointer moving(trivial push/pop operations) | Heap is slower because of more complex allocation/deallocation |
Stack is a memory allocated for a thread of execution; LIFO | Heap is a memory allocated dynamically |
On function call a block is reserved on top of the stack for local variables, when function returns the block is freed |
In C++ malloc
, calloc
and new
are used to allocate memory on the heap and it's responsible for memory leaks if not manually deallocated. C++ heap memory leak example:
int foo()
{
char *pBuffer; // Nothing allocated yet (excluding the pointer itself, which is allocated here on the stack)
bool b = true; // Allocated on the stack
if(b)
{
// Create 500 bytes on the stack
char buffer[500];
// Create 500 bytes on the heap
pBuffer = new char[500];
} // <- Buffer is deallocated here, pBuffer is not
} // <- Oops there's a memory leak, I should have called delete[] pBuffer;
In Go a compiler might choose to allocate local variables on the heap or on the stack, but contrary to C++ the choice is not determined by whether var
or new
was used to declare the variable.
var global *int
func f() {
var x int
x = 1
global = &x
}
Here, x
must be heap-allocated because it is still reachable from the variable global
after f
has returned, despite being declared as a local variable; we say x
escapes from f
.
func g() {
y := new(int)
*y = 1
}
Conversely, when g
returns, the variable *y
becomes unreachable and can be recycled. Since *y
does not escape from g
, it's safe for the compiler to allocate *y
on the stack, even though it was allocated with new
. In any case, the notion of escaping is not something that you need to worry about in order to write correct code, though it's good to keep in mind during performance optimization, since each variable that escapes requires an extra memory allocation.
Here is another example of the local variable within the function which escapes:
func main() {
s := sum(1, 2, 3, 4, 5)
fmt.Println("The sum is ", *s)
}
func sum(values ...int) *int {
result := 0
for _, v in range values {
result += v
}
return &result
}