Garbage collection frees you from memory management, but it also limits your control. What if you want to implement a cache that automatically shrinks when memory is tight? Normally, garbage collector don’t free up memory when there are pointers to the data, but Go 1.24 added weak pointers, which allow you to signal to the garbage collector that some data is expendable.
A weak pointer is a special type in the standard library’s weak package that wraps a pointer. When the garbage collector runs, it checks whether the only live references to a block of memory are wrapped in weak pointers. If so, it may reclaim the memory and set the weak pointer’s wrapped pointer to nil.
You can create a weak pointer by passing a pointer to the Make function in the weak package, which returns a weak.Pointer instance. The underlying pointer is accessed via the Value method. Here’s a code snippet that demonstrates the creation and lifespan of weak pointers:
m := map[int]weak.Pointer[int]{}
for i := range 10 {
m[i] = weak.Make(&i)
}
// five shares a pointer with the underlying value
var five *int = m[5].Value()
// two refers to the weak pointer
var two weak.Pointer[int] = m[2]
fmt.Println(five, *five, m[5].Value(), *m[5].Value())
fmt.Println(two.Value(), *two.Value(), m[2].Value(), *m[2].Value())
runtime.GC()
fmt.Println()
fmt.Println(five, *five, m[5].Value(), *m[5].Value()) // always still non-nil
fmt.Println(two.Value(), m[2].Value()) // may be nil after gc
In the sample code, a map is populated with weak pointers that wrap int pointers. Two variables are created to refer to values in the map: five is an *int that points at the same memory as the pointer wrapped by the weak pointer stored at m[5]. The variable two is a weak.Pointer[int]. The call to runtime.GC() forces the garbage collector to run, and then the code prints out the contents of five, two, and their associated map values.
It’s guaranteed that m[5].Value() will never have a nil value, because the variable five points to the same memory. However, two.Value() and m[2].Value() might be nil, because both are weak pointers and there are no direct pointers to the weak pointers’ values. (You cannot control when a pointer that is only referred to by weak pointers is freed; it’s entirely up to the garbage-collection algorithm.)
You can run this code for yourself on the Go playground.
Note
The concept of weak pointers (sometimes called weak references) isn’t specific to Go. Many languages, including C++, Rust, Swift, Python, Java, and JavaScript have weak references. (Java has multiple levels of weak references, because of course it does.)
Be careful when using weak pointers; always check to see if their value is nil before using them. The Guide to the Go Garbage Collector contains a section on how and when to use weak pointers. Read the Go team’s blog post introducing weak pointers for additional details.