Software Development

Value of pass by value in GoLang

Now we are getting in some of the core concepts! As we know, it is very important to understand the impact that Go program will have on the machine.

Everything is passed by value in Go, no matter what you pass. What you see is what you get.

Each go routine (i.e path of execution) get Stack, which is a continuous memory. Go routine needs the stack to do all the allocation required. We will learn about go routine later, it is just like a thread but much more lighter.

As go routine executes a function it starts getting a slice or a portion of memory from the stack that was allocated.

Lets try to understand this with a simple example

func main() {

  counter := 0

  counter++
 fmt.Println("In main", counter)
 inc(counter)
 fmt.Println("After inc", counter)
}
value in GoLang

A Function can only read/write to its stack frame, that is the reason why function parameters are required.

With the above example, any change done by the inc function is local to that stack frame and if it wants to share it to the caller then it has to return it so that value can be copied to the caller frame.

Other interesting properties about stack frame is that it is reusable for eg after inc function completes execution that stack frame is available to another function.

So it is like increment pointer in stack to allocate memory to function and once that function completes then decrements the counter to mark memory as free.

Pass by value is required for safety and to reason about code which is missing in many language.

Lets explore all these changes when pointer or address of variable is passed to function.

Lets try to understand how stack frame looks when below code is executed

 func main() {

 counter := 0

 fmt.Println("Before pointer inc ", counter)
 incByPointer(&counter)
 fmt.Println("After pointer inc ", counter)
}
value in GoLang

In the above example parameter to function is still passed by value but this time it is of address type.

Caller knows that it has received address (&variable) of variable and to change the value it has to use different instruction (*variable) 

An asterisk (*) operator allow the program to change the variable that is outside of its own stack frame. This variable can be in heap or in caller function stack.

Having clear distinction when value is passed vs address of value is a very power full thing as it tells which function is doing read vs write.

Anytime you see the pointer (&) it is very clear that some mutation is happening in function.

No magical modification is possible.

Having clear distinction has couple of advantages:

 – Compiler can do escape analysis to determine what gets allocated to stack vs heap. This keeps GC happy because stack allocation is cheap and heap has GC overhead

 – When to copy value vs share value. This is a very useful thing for large values, you don’t want to copy 1gb of buffer to function.

Go lang gives options to the developer to choose trade off rather than giving no control.

Lets look at one more example on how allocation works:

func allocateOnStack() stock {

 google := stock{symbol: "GOOG", price: 1109}
 return google
}

func allocateOnHeap() *stock {

 google := stock{symbol: "GOOG", price: 1109}
 return &google
}

Both of the above function is creating stock value but look at return type: one returns value(allocateOnStack) and other one (allocateOnHeap) returns address.

Compiler looks the return type and make a decision on what goes on stack vs heap.

So you decided what you want to throw at GC vs keep it happy.

You might have question on Stack like how big is stack ?

Each Go routine starts with 2 MB stack size, it is small and good enough to hold lots of functions call.

For most of the cases 2 MB is good but if program continues to put memory pressure on Stack then it grows to adjust the need only for specific Go routine.

Stack growth has allocation & copy cost, it is just like allocate new array and copy the value from previous array.

One nice thing about Stack memory is that it is monitored by GC and it will reduce the size of Stack if utilization of stack is around 25%.  

Go gives power of compact memory layout using Struct and efficient memory allocation using pass by value.

All the samples used in this blog is available @ pointers github repo

Published on Java Code Geeks with permission by Ashkrit Sharma, partner at our JCG program. See the original article here: Value of pass by value in GoLang

Opinions expressed by Java Code Geeks contributors are their own.

Ashkrit Sharma

Pragmatic software developer who loves practice that makes software development fun and likes to develop high performance & low latency system.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button