[Golang (Go)] Use sync.Pool to cache temporary objects for later reuse relieving pressure on the garbage collector

sync.Pool

A Pool is a set of temporary objects that may be individually saved and retrieved.

When there is an expensive object you have to create it frequently, it can be very beneficial to use sync.Pool.

Any item stored in the Pool may be removed automatically at any time without notification. If the Pool holds the only reference when this happens, the item might be deallocated.

A Pool is safe for use by multiple goroutines simultaneously.

Pool’s purpose is to cache allocated but unused items for later reuse, relieving pressure on the garbage collector. That is, it makes it easy to build efficient, thread-safe free lists. However, it is not suitable for all free lists.

An appropriate use of a Pool is to manage a group of temporary items silently shared among and potentially reused by concurrent independent clients of a package. Pool provides a way to amortize allocation overhead across many clients.

An example of good use of a Pool is in the fmt package, which maintains a dynamically-sized store of temporary output buffers. The store scales under load (when many goroutines are actively printing) and shrinks when quiescent.

On the other hand, a free list maintained as part of a short-lived object is not a suitable use for a Pool, since the overhead does not amortize well in that scenario. It is more efficient to have such objects implement their own free list.

A Pool must not be copied after first use.

1
2
3
4
5
6
7
8
type Pool struct {

// New optionally specifies a function to generate
// a value when Get would otherwise return nil.
// It may not be changed concurrently with calls to Get.
New func() interface{}
// contains filtered or unexported fields
}

Two methods defined on Pool:

  • func (p *Pool) Get() interface{}

    Get selects an arbitrary item from the Pool, removes it from the Pool, and returns it to the caller. Get may choose to ignore the pool and treat it as empty. Callers should not assume any relation between values passed to Put and the values returned by Get.

    If Get would otherwise return nil and p.New is non-nil, Get returns the result of calling p.New.

  • func (p *Pool) Put(x interface{})

    Put adds x to the pool.

Examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"sync"
)

// A dummy struct
type Person struct {
Name string
}

// Initializing pool
var personPool = sync.Pool{
// New optionally specifies a function to generate
// a value when Get would otherwise return nil.
New: func() interface{} { return new(Person) },
}

// Main function
func main() {
// Get hold of an instance
newPerson := personPool.Get().(*Person)
// Defer release function
// After that the same instance is
// reusable by another routine
defer personPool.Put(newPerson)

// Using the instance
newPerson.Name = "Jack"
}

Limitations

Because we cannot make any assumptions about the elements stored in sync.Pool, the following things can happen:

  • The object in the Pool may be released at any time, and the release strategy is completely managed internally by the runtime;

  • The object obtained by Get may have just been created, or it may have been cached before. The user cannot distinguish;

  • You cannot know the number of objects in the Pool;

  • When you run out of an instance taken from the Pool, you must remember to call Put, otherwise the Pool cannot reuse the instance, usually this is done with defer;

  • The object must be reset to the initialized state before putting it back. Otherwise, when others use it, they will find the smell.

Therefore, only if your scenario satisfies the above assumptions can you use Pool correctly. The essential purpose of sync.Pool is to increase the reuse rate of temporary objects and reduce the GC burden.

Key points: Temporary objects.

Therefore, a stateful, long-term effective resource like socket is not suitable for Pool.

References

[1] Pool | sync - The Go Programming Language - https://golang.org/pkg/sync/#Pool

[2] Go: Understand the Design of Sync.Pool | by Vincent Blanchon | A Journey With Go | Medium - https://medium.com/a-journey-with-go/go-understand-the-design-of-sync-pool-2dde3024e277

[3] Go: The Idea Behind Sync.Pool. I encountered a problem in Go Garbage… | by Ali Josie | The Startup | Medium - https://medium.com/swlh/go-the-idea-behind-sync-pool-32da5089df72

[4] Go - sync.Pool | go Tutorial - https://riptutorial.com/go/example/16314/sync-pool