Golang Escape Analysis: reduce pressure on GC!

Sakthi Saravanan
Sep 28, 2019 · 4 min read

To start with, we know that on high-level, there are 2 kinds of memories used by our running software. i.e. as below

  • Stack
  • Heap

Stack Memory:

This is a block of memory allotted for each function to execute its defined task. Size of this memory block is decided at the compile time.

Heap Memory:

This is a block of memory allotted at runtime which is demanded by our software.

In languages like C, if you use functions like ‘new’ & ‘malloc’, the memory would be allocated in heap.

Not in Golang, even if you use function ‘new’ to allocate memory, it might not be allocated in heap memory. It could possibly take a place in stack memory itself. This decision will be made by Go compiler. But why?

When you say memory is dynamically allotted at runtime, then it has to be properly freed up once its purpose is done. Otherwise, at one point in time, our application will run out of memory resources and it could lead to a crash. Next question might come to your mind is, Go is garbage collected (GC) language. Why should we worry about it? GC would do its job of collecting the inaccessible memory and put them back to free bucket.

Still, it is important to effectively use heap memory. Reason being, when GC starts running frequently, our application routines gets a lesser share of CPU cores compared to GC. When you put pressure on GC to collect more memory from the heap, then it could possibly slow down our application performance (which may not be the case if we would have cautiously written our code).

So what is Escape Analysis? What is the intelligence it provides to the Golang compiler? It checks a memory that it really needs to be allocated at a heap or it could be managed within a stack itself. The decision would be made by checking whether a particular memory needs to be shared across stack frames (function boundaries). If not, it would place it in a stack, else in heap memory. We will try understanding this by a simple program.

package mainimport "fmt"func main() {
a := new(int)
b := new(int)

sum := *a + *b
fmt.Println("sum = ", sum)
}

Above program does simple a thing, the addition of two number ‘a’ and ‘b’ and then prints it. Lines that we have interested in the above program are the declaration of ‘a’ and ‘b’ using new(). Though memory is allocated using ‘new()’ it would not be allocated in heap memory. We could verify through compiler flag ‘gcflags’ with build command. When you run the command “go build -gcflags “-m”, you would get an output as below:

...
./escapeanalysis.go:6:10: main new(int) does not escape
./escapeanalysis.go:7:10: main new(int) does not escape

...

Highlighted lines in the above output confirm that ‘a’ and ‘b’ doesn’t escape to the heap. Let’s do a small modification to our program and see the output. This time, we are just going to print the reference ‘a’ instead of the value, just after declaration. (by mistake we are printing the reference, but intension is to print the value)

package mainimport "fmt"func main() {
a := new(int)
fmt.Println("value of a: ", a)
b := new(int)
sum := *a + *b
fmt.Println("sum = ", sum)
}

The highlighted line below confirms that it is escaped to the heap. Means ‘a’ needs to be shared across the function stack frames [between main() and Println()]

...
./escapeanalysis.go:7:14: a escapes to heap
./escapeanalysis.go:6:10: new(int) escapes to heap
...

Let’s change the print statement to print the value instead of a reference that is done by mistake.

package mainimport "fmt"func main() {
a := new(int)
fmt.Println("value of a: ", *a)
b := new(int)
sum := *a + *b
fmt.Println("sum = ", sum)
}

Now ‘a’ does not escape to the heap. So less pressure on GC and better performance for an application.

...
./escapeanalysis.go:10:14: sum escapes to heap
./escapeanalysis.go:6:10: main new(int) does not escape
...

This is one of the cool stuff in Golang, we could run escape analysis and see how we could optimize our application performance. Hope this blog helps you to understand a bit about different aspects like a heap, GC and escape analysis!

“Quality is never an accident; it is always the result of intelligent effort.” — John Ruskin.

Follow us on Twitter 🐦 and Facebook 👥 and join our Facebook Group 💬.

To join our community Slack 🗣️ and read our weekly Faun topics 🗞️, click here⬇

Faun

The Must-Read Publication for Aspiring Developers & DevOps Enthusiasts. Medium’s largest DevOps publication.

Sakthi Saravanan

Written by

Software Developer

Faun

Faun

The Must-Read Publication for Aspiring Developers & DevOps Enthusiasts. Medium’s largest DevOps publication.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade