sync.Pool

sync.Pool is a type provided by the go standard library in the sync package. It implements a pool of reusable objects. The primary purpose of sync.Pool is to reduce the overhead of allocating and deallocating objects frequenty by providing a pool where objects can be reused.

Why does it matter ? Because object allocation and garbage collection can be expensive, especially in high performance applications or scenarios with frequent allocations. sync.Pool helps maintaining this by mitigating a pool of objects that can be reused, reducing the need for frequent allocations and garbage collection.

  • Key Concepts of sync.Pool:

    • Object Pooling
    • Object Retrieval and Return
  • Methods of sync.Pool:

    • Get()
    • Put(interface{})
    • New(Optional)
  • It works on the LIFO principle.

  • The new field will create a new instance if the object pool is empty.

Code

package main

import (
	"fmt"
	"sync"
)

type person struct{
	name string
	age int
}

func main() {
	poolWithNew()
	poolWithoutNew()

}

func poolWithoutNew(){

	var pool = sync.Pool{}
	pool.Put(&person{name: "John", age: 26})
	person1 := pool.Get().(*person)

	fmt.Println("Person 1:", person1)


	fmt.Printf("Person1: Name: %s | Age: %d\n", person1.name, person1.age)

	pool.Put(person1)
	fmt.Println("Returned Person to Pool")

	person2 := pool.Get().(*person)
	fmt.Println("Got Person 2:", person2)

	person3 := pool.Get()
	if person3 != nil {
	fmt.Println("Got Person 3:", person3)
	person3.(*person).name = "James"
	} else {
		fmt.Println("Sync Pool is empty. So person3 is not assigned anything")
	}

	// Returning object to the pool again
	pool.Put(person2)
	pool.Put(person3)

	person4 := pool.Get().(*person)
	fmt.Println("Got Person 4:", person4)

	person5 := pool.Get()
	if person5 != nil {
	fmt.Println("Got Person 3:", person5)
	person5.(*person).name = "James"
	} else {
		fmt.Println("Sync Pool is empty. So person5 is not assigned anything")
	}
}

func poolWithNew(){

	var pool = sync.Pool{
		New: func() interface{}{
			fmt.Println("Creating a new Person")
			return &person{}
		},
	}

	// Get an Object from the pool
	person1 := pool.Get().(*person)
	person1.name = "John"
	person1.age = 18
	fmt.Println("Person 1:", person1)

	fmt.Printf("Person1: Name: %s | Age: %d\n", person1.name, person1.age)

	pool.Put(person1)
	fmt.Println("Returned Person to Pool")

	person2 := pool.Get().(*person)
	fmt.Println("Got Person 2:", person2)

	person3 := pool.Get().(*person)
	fmt.Println("Got Person 3:", person3)
	person3.name = "James"

	// Returning object to the pool again
	pool.Put(person2)
	pool.Put(person3)

	person4 := pool.Get().(*person)
	fmt.Println("Got Person 4:", person4)

	person5 := pool.Get().(*person)
	fmt.Println("Got Person 5:", person5)
}

Key Notes:

  • Best Practices for using sync.Pool:

    • Use for expensive object allocations
    • Keep Objects in Pool Clean
    • Avoid Complex Objects
  • Advanced Use Cases

    • Reusing Buffers
    • Managing Database Connections
    • High Performance Applications
  • Considerations and Limitations

    • Garbafe Collection
    • Not for Long-Lived Objects
    • Thread Safety