Go 1.24
introduces a new std-lib
package called weak
, which allows you to create safe references to *T
without preventing the garbage collector (GC) from reclaiming the memory associated with *T
.
The
weak
package provides ways to safely reference memory weakly without preventing the garbage collector’s reclamation.
Much like OS.ROOT
, weak
is a feature that has existed in other programming languages for some time, including:
- In
Java
,WeakReference
andSoftReference
are classic implementations primarily used for caching and object pools. These references allow automatic garbage collection when the JVM detects a memory shortage. - In
Python
, theweakref
module allows the creation of weak references, commonly used to prevent circular reference issues or for caching. - In
C++
,std::weak_ptr
was introduced alongsidestd::shared_ptr
to solve circular dependency problems with shared pointers. - In
Rust
,Weak
is the weak reference version ofRc
andArc
, which helps prevent circular references and provides more flexible memory management.
Simple Definition of weak
The weak
package is defined, with just a Make
method and a Value
method.
By using weak.Make
, a weak.Pointer
is created, and if the original *T
has not been GC, we can access its address through weak.Pointer.Value
. If the object has been collected, the Value
will return nil
.
You can see an example implementation of weak
here: Example Code on Gist
Example Output:
|
|
In this example:
- We create a
string
variableoriginalObject
and useweak.Make
to create aweak.Pointer
calledweakPtr
. - During the first garbage collection (GC), since
originalObject
is still in use,weakPtr.Value
returns the address oforiginalObject
. - In the second GC, since
originalObject
is no longer used, it is collected by the GC, andweakPtr.Value
returnsnil
.
Also, runtime.AddCleanup
, a new feature in Go 1.24, works similarly to runtime.SetFinalizer
by allowing you to execute code when an object is garbage collected. This feature will be covered in more detail in a later post.
Key Takeaways:
weak.Make
creates aweak.Pointer
, which hides the real memory address but does not affect the GC.- If the real object is garbage collected,
weak.Pointer.Value
will returnnil
. Since we don’t know when the real object will be reclaimed, always check the return value ofweak.Pointer.Value
.
The Practical Use of weak
Canonicalization Maps
You might remember the unique
feature introduced in Go 1.23, which uses a single pointer (8 bytes) to represent multiple identical strings, saving memory. The weak
package can achieve similar functionality (In fact, in Go 1.24, unique
has been refactored using weak
).
Implementing a Fixed-Size Cache
|
|
In this example, we use a fixed-size list and a Map
to store the position of each key in the list, where the value is a weak.Pointer
to a list.Element
. When we add a new cache item, we first check if the list is full. If so, the oldest item is evicted. When a key exists in the cache, Map[key].Value
will return the address of the list element. If the item is evicted, Map[key].Value
returns nil
.
This design helps to create an efficient, fixed-size cache system. Using weak
with a lock-free queue structure allows for even more efficient data handling.
I find weak
to be quite helpful in specific scenarios. It’s easy to use, and I plan to incorporate it into my projects.