Stack, Heap and Escape Analysis
Last updated
Last updated
The Go compiler uses escape analysis to determine which variables should be allocated on the stack of a goroutine and which variables should be allocated on the heap.
The way a variable is used - not declared - determines whether it lives on the stack or the heap.
Sharing up (returning pointers) typically escapes the Heap.
Go encourages the use of value types, which are allocated on the stack, as they don't require garbage collection.
The compiler will try to allocate a variable to the local stack frame of the function in which it is declared. However, it is also able to perform escape analysis: if it cannot prove that a variable is not referenced after the function returns, then it allocates it on the heap instead.
Output: 42
The heap footprint increases with time. At a certain point, the GC runs and cleans up the heap, before it starts growing again.
We can rewrite the program so that there is no reference to a
outside of the stack frame, then the variable doesn’t escape to the heap.
a
does not escape to the heap, since it is not modified “above” f
’s stack frame, (only “below”, in g
’s).
If we execute g
in a goroutine, we find that a
escapes to the heap.
This is because each goroutine has its own stack. As a result, the compiler cannot guarantee that f
’s stack hasn’t been popped (invalidating a
) when g
accesses it. Therefore, the variable must live on the heap.
The function main
has its local variables n and n2. main
function is assigned a stack frame in the stack. Same goes for the function square
.
Now as soon as the function square
returns the value, the variable n2
will become 16
and the memory function square
is NOT cleaned up, it will still be there and marked as invalid (unused).
Now when the Println
function is called, the same unused
memory on stack left behind by the square function is consumed by the Println
function.
Resources:
Stack | Heap |
---|---|
space for the execution of thread;
when a function in called, a block
(stack frame) is reserved on top of the stack for local variables and some bookkeeping data;
when a function returns, the block becomes unused and can be used the next time any function is called
requires manual housekeeping of what memory is to be reserved and what is to be cleaned the memory allocator will perform maintenance tasks such as defragmenting allocated memory or garbage collecting
initial stack memory allocation is done by the OS at compile time
allocated at run time