In my previous blog, I talked about the basics of Golang and how you can write a simple program in Go. This time, I will elaborate more on the fundamentals of the Go language with a focus on features of Go that can come in handy and how Go differs from traditional programming languages.
Let’s start with the features first.
Features of Golang
Golang is gaining popularity because of its speed and straightforwardness. Features like its runtime environment, garbage collection, and concurrency approach are loved by most programmers. Let's look at the features in detail.
The runtime package contains operations that interact with the Go runtime. It is compiled into every Go binary. The Go runtime is the software stack responsible for building and running code in a flexible environment. This package is used to control things such as goroutines, garbage collection, reflection, scheduling, memory allocation, concurrency support, and networking - all functions essential to the operation of the language.
The Go runtime proactively returns unwanted memory to the operating system and reduces excess memory consumption, reducing the chances of out-of-memory errors. It helps minimize idle memory consumption by up to 20%.
The following are a few of the environment variables that control the runtime behavior of Go programs. Their meanings and usage may, however, change from release to release. I have described them in short below:
GOGC: This variable sets the initial garbage collection target percentage. Setting GOGC=off entirely disables the garbage collector.
GOMEMLIMIT: It sets a soft memory limit for the runtime. This memory limit includes the Go heap and all other memory managed by the runtime.
GODEBUG: This variable controls debugging variables within the runtime. It is a comma-separated list of names=Val pairs. The net and net/http packages also refer to debugging variables in GODEBUG.
GOMAXPROCS: It limits the number of operating system threads that can execute user-level Go code simultaneously.
GOARCH, GOOS, GOPATH, and GOROOT: These environment variables complete the set of Go environment variables.
Efficient garbage collection:
Garbage collection is a mechanism to find memory space that is no longer needed. It plays an important role of freeing up memory that can be used for another purpose where it is required. The garbage collector is responsible for tracking allocated heap memory spaces. The implementation of these systems is constantly changing & evolving and varies by technology. This allocation and deallocation of memory space are called memory management.
Each programming language has its mechanism to manage memory. Only a few provide internal support for memory management like Golang does. In some technologies, we need to handle this memory deallocation separately. In language C, a programmer can use the 'malloc' and 'calloc' functions to allocate memory and function 'free' to deallocate the assigned memory, which is no longer used. This memory management method is called explicit deallocation and is quite powerful. Over the years, Go has seen many changes to its memory management and is likely to see more changes in the future.
Modern programming languages like Java and Golang focus on different goals, and programmers can achieve goals by focusing only on business logic and optimized implementation. Therefore, an automatic garbage collection mechanism is an essential upgrade to the principles of programming in these languages.
Go relies on a garbage collector. There are two critical parts while working in a particular garbage collector - a mutator and a collector. The collector executes garbage collection logically and finds objects that should have their memory freed. The mutator executes the application code and allocates new objects to the heap. It also updates existing objects on the heap as the program runs, which includes making some objects unreachable when they're no longer needed. Go's garbage collector is a non-generational concurrent, tri-color mark and sweep garbage collector.
As programmers, we all know about concurrent and parallel execution. In concurrent systems, multiple actions can be in progress (may not be executed) simultaneously. Meanwhile, in parallel systems, multiple actions are performed simultaneously. In fact, concurrency and parallelism are conceptually overlapped to some degree, but "in progress" clearly makes them different. In other languages like Java, multiple threads execute simultaneously to achieve concurrent execution.
In multithreading, the thread is the execution entity that requires 1 MB of stack memory to initialize the stack. This size is fixed. Threads do not have growable segmented stacks. This thread is managed by the operating system's Kernel.
Then what is different in Golang? Golang has introduced the lightweight execution entity to achieve concurrency efficiently, which is "goroutine." A goroutine is a function or method which executes independently and simultaneously in connection with any other goroutines present in the program. It starts with only 2kB of stack memory and can grow and shrink according to the application's needs. So, to process a task, thousands of goroutines can multiplex over the thread very easily. If any goroutine in that thread blocks, say, waiting for any input, another OS thread is created, and the remaining Goroutines are shifted to the new OS thread.
All these are taken care of by the runtime and programmers do not need to care about this switching process. Communication between multiple goroutines is simple by using the channels in Go. Channels by design prevent race conditions when accessing shared memory using goroutines. Channels can be thought of as a pipe using which goroutines communicate.
What makes Golang different from other programming languages
Let's look at how Go compares to another conventional programming language - Java. While Golang is a general-purpose language that is not object-oriented or procedure-oriented, Java is a purely object-oriented language. Golang is much faster than Java solely because of its compilation process. Java performs much slower because it uses Java virtual machine to interpret already compiled code. They handle errors quite differently, too. Like Java, Go does not have exceptions. It has a mechanism known as defer, panic, and recover. Java possesses an exception-handling mechanism that is powerful and maintains a normal flow of applications. When it comes to parallelly running applications, Go supports concurrency using goroutines concurrency while Java allows for threads to execute programs. Concurrency in Golang is highly effective because of lightweight goroutines. In Java, threads need 1 MB of stack memory to initiate. This is an expensive affair. Go doesn't support private and public keywords. The first letter of the function name decides if it's Public (upper case) or private (lower case). In Java, methods can be public or private.
That's all for now about the Golang features that you can leverage. Let us know your thoughts in the comments below. We will be coming up with more exciting blogs on Golang.