Garbage Collection in Go
Garbage collection is an automated memory management process that frees up memory in a computer’s heap that is no longer being used by the program.
In programming languages like Java, C#, and Python, the memory allocation and deallocation process is handled by the language runtime environment, which uses garbage collection to automatically reclaim memory used by objects that are no longer needed by the program.
Garbage collection works by periodically scanning the heap for objects that are no longer reachable, i.e., they cannot be accessed by the program, and deallocating the memory used by those objects. This frees up memory that can be used by other objects or by the program itself.
Garbage collection is important because it helps to prevent memory leaks, where a program continues to allocate memory without freeing it, eventually leading to the program running out of memory and crashing. With garbage collection, memory management is handled automatically, allowing developers to focus on writing code rather than worrying about memory management.
If you want to manually trigger garbage collector(GC) you can use runtime.GC()
function to do so.
package main
import (
"fmt"
"runtime"
)
func main() {
// Print the initial memory statistics
printMemStats()
// Allocate a large slice of memory
var data []int
for i := 0; i < 1000000; i++ {
data = append(data, i)
}
// Print the memory statistics after allocating memory
printMemStats()
// Force a garbage collection cycle
runtime.GC()
// Print the memory statistics after garbage collection
printMemStats()
}
func printMemStats() {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
fmt.Printf("Alloc = %v MiB, TotalAlloc = %v MiB, Sys = %v MiB, NumGC = %v\n",
bToMb(mem.Alloc), bToMb(mem.TotalAlloc), bToMb(mem.Sys), mem.NumGC)
}
func bToMb(b uint64) uint64 {
return b / 1024 / 1024
}
----------------------------
Alloc = 0 MiB, TotalAlloc = 0 MiB, Sys = 7 MiB, NumGC = 0
----------------------------
Alloc = 8 MiB, TotalAlloc = 39 MiB, Sys = 32 MiB, NumGC = 0
----------------------------
Alloc = 0 MiB, TotalAlloc = 39 MiB, Sys = 32 MiB, NumGC = 9
----------------------------
Here
Alloc
: the number of bytes allocated and still in useTotalAlloc
: the cumulative number of bytes allocatedSys
: the total amount of memory obtained from the operating systemNumGC
: the number of garbage collection cycles performed
First allocated memory is 0 and no GC started (NumGC) same for second time but the allocated memory increases and Total allocated memory also increases. And on the Third time the memory allocation decreases to 0 and we can see the GC was triggered and 9GC was started.
These values can be useful for profiling and optimizing your Go program’s memory usage. For example, if you notice that the allocated memory is growing continuously over time, it may indicate that your program is leaking memory and needs to be optimized to reduce memory usage. Similarly, if the Idle memory is consistently low, it may indicate that your program is allocating more memory than it actually needs, and may benefit from memory optimization techniques like object pooling or recycling.
In Go, new()
and make()
are both used for memory allocation, but they have different use cases.
new()
is a built-in function in Go that allocates memory for a new value of a specified type and returns a pointer to it. It initializes the memory to zero value of the type, and it is typically used to create a pointer to a struct or any other custom data type.
For example, p := new(int)
creates a new integer pointer p
, which is initialized to the zero value of an integer (which is 0
). Since new()
returns a pointer to the newly allocated memory, we can use the *
operator to access the value stored in that memory location, like *p = 42
to set the value of the integer pointed to by p
.
make()
is also a built-in function in Go that is used to allocate and initialize a slice, map, or channel. Unlike new()
, make()
returns an initialized value of the specified type, not a pointer to it.
For example, s := make([]int, 10)
creates a new slice of 10 integers, with all elements initialized to zero. We can then use s[0] = 1
to set the first element of the slice to 1.
In summary, use new()
when you need to allocate memory for a custom data type and return a pointer to it, and use make()
when you need to allocate and initialize a slice, map, or channel.
Python uses two strategies to implement garbage collection. One is Reference Counting(RC), and the other one is the Automatic Garbage Collection. We can also invoke the garbage collection in Python manually using the
gc
module.
In Go, the garbage collector uses a combination of mark-and-sweep
and concurrent mark-and-sweep
garbage collection strategies.
In the mark-and-sweep
phase, the garbage collector scans the program’s memory for objects that are no longer being used, marks them as “garbage”, and then sweeps through the memory to free up the memory used by those objects.
In the concurrent mark-and-sweep
phase, the garbage collector works in parallel with the program’s execution to continue marking and sweeping unused objects while the program is running. This helps to reduce the impact of garbage collection on the program’s performance and responsiveness.
Go’s garbage collector also uses a generational garbage collection strategy
, where objects are divided into several generations based on their age. The youngest generation is collected more frequently, while older generations are collected less frequently.
Overall, the combination of mark-and-sweep, concurrent mark-and-sweep, and generational garbage collection strategies used in Go’s garbage collector is designed to be efficient and reliable, while minimizing the impact on the program’s performance and responsiveness.