Documentation
¶
Overview ¶
Package stealthpool provides a memory pool that allocates blocks off-heap that will NOT be tracked by the garbage collector. The name stealthpool comes from the fact that the memory being allocated by the pool is `stealthy` and will not be garbage collected ever.
These blocks should be used in situations where you want to keep garbage collection to a bare minimum. Needless to say, since the GC will not track any of the memory, extreme care must be had in order to avoid memory leaks:
pool, _ := stealthpool.New(1) // ... defer pool.Close() // always call Close to avoid memory leaks
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrPoolFull is returned when the maximum number of allocated blocks has been reached ErrPoolFull = errors.New("pool is full") // ErrPreallocOutOfBounds is returned when whe number of preallocated blocks requested is either negative or above maxBlocks ErrPreallocOutOfBounds = errors.New("prealloc value out of bounds") // ErrInvalidBlock is returned when an invalid slice is passed to Return() ErrInvalidBlock = errors.New("trying to return invalid block") )
Functions ¶
This section is empty.
Types ¶
type Pool ¶
Pool is the off heap memory pool. It it safe to be used concurrently
func New ¶
New returns a new stealthpool with the given capacity. The configuration options can be used to change how many blocks are preallocated or block size. If preallocation fails (out of memory, etc), a cleanup of all previously preallocated will be attempted
Example ¶
package main import ( "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(2) if err != nil { panic(err) } defer pool.Close() block, err := pool.Get() if err != nil { panic(err) } fmt.Printf("len(block): %d cap(block): %d\n", len(block), cap(block)) }
Output: len(block): 4096 cap(block): 4096
Example (CustomBlockSize) ¶
package main import ( "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(2, stealthpool.WithBlockSize(8*1024*1024)) if err != nil { panic(err) } defer pool.Close() block, err := pool.Get() if err != nil { panic(err) } fmt.Printf("len(block): %d cap(block): %d\n", len(block), cap(block)) }
Output: len(block): 8388608 cap(block): 8388608
Example (Preallocation) ¶
package main import ( "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(2, stealthpool.WithPreAlloc(2)) if err != nil { panic(err) } defer pool.Close() fmt.Printf("free blocks: %d total allocated: %d\n", pool.FreeCount(), pool.AllocCount()) block, err := pool.Get() if err != nil { panic(err) } fmt.Printf("len(block): %d cap(block): %d\n", len(block), cap(block)) }
Output: free blocks: 2 total allocated: 2 len(block): 4096 cap(block): 4096
func (*Pool) AllocCount ¶
AllocCount returns the total number of allocated blocks so far
Example ¶
package main import ( "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(1) if err != nil { panic(err) } fmt.Printf("total allocated: %d\n", pool.AllocCount()) _, _ = pool.Get() fmt.Printf("total allocated: %d\n", pool.AllocCount()) pool.Close() newPool, err := stealthpool.New(3, stealthpool.WithPreAlloc(2)) if err != nil { panic(err) } fmt.Printf("total allocated: %d\n", newPool.AllocCount()) newPool.Close() }
Output: total allocated: 0 total allocated: 1 total allocated: 2
func (*Pool) Close ¶
Close will cleanup the memory pool and deallocate ALL previously allocated blocks. Using any of the blocks returned from Get() after a call to Close() will result in a panic
Example ¶
package main import ( "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(2) if err != nil { panic(err) } block1, err := pool.Get() if err != nil { panic(err) } block2, err := pool.Get() if err != nil { panic(err) } if err := pool.Close(); err != nil { panic(err) } // using the slices after Close() will panic fmt.Println(block1[:5]) fmt.Println(block2[3:]) }
Output:
func (*Pool) FreeCount ¶
FreeCount returns the number of free blocks that can be reused
Example ¶
package main import ( "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(1) if err != nil { panic(err) } fmt.Printf("free blocks: %d\n", pool.FreeCount()) block, _ := pool.Get() fmt.Printf("free blocks: %d\n", pool.FreeCount()) _ = pool.Return(block) fmt.Printf("free blocks: %d\n", pool.FreeCount()) pool.Close() newPool, err := stealthpool.New(3, stealthpool.WithPreAlloc(2)) if err != nil { panic(err) } fmt.Printf("free blocks: %d\n", newPool.FreeCount()) newPool.Close() }
Output: free blocks: 0 free blocks: 0 free blocks: 1 free blocks: 2
func (*Pool) Get ¶
Get returns a memory block. It will first try and retrieve a previously allocated block and if that's not possible, will allocate a new block. If there were maxBlocks blocks already allocated, returns ErrPoolFull
Example ¶
package main import ( "errors" "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(1) if err != nil { panic(err) } defer pool.Close() block, err := pool.Get() if err != nil { panic(err) } fmt.Printf("len(block): %d cap(block): %d\n", len(block), cap(block)) fmt.Printf("free blocks: %d total allocated: %d\n", pool.FreeCount(), pool.AllocCount()) _, err = pool.Get() fmt.Printf("pool is full: %t\n", errors.Is(stealthpool.ErrPoolFull, err)) }
Output: len(block): 4096 cap(block): 4096 free blocks: 0 total allocated: 1 pool is full: true
func (*Pool) Return ¶
Return gives back a block retrieved from Get and stores it for future re-use. The block has to be exactly the same slice object returned from Get(), otherwise ErrInvalidBlock will be returned.
Example ¶
package main import ( "errors" "fmt" "github.com/Link512/stealthpool" ) func main() { pool, err := stealthpool.New(1) if err != nil { panic(err) } defer pool.Close() block, err := pool.Get() if err != nil { panic(err) } fmt.Printf("free blocks: %d total allocated: %d\n", pool.FreeCount(), pool.AllocCount()) err = pool.Return(block[1:]) fmt.Printf("resliced block is invalid: %t\n", errors.Is(stealthpool.ErrInvalidBlock, err)) // once block is returned, the pool will re-use it in a subsequent Get call err = pool.Return(block) if err != nil { panic(err) } fmt.Printf("free blocks: %d total allocated: %d\n", pool.FreeCount(), pool.AllocCount()) newBlock, err := pool.Get() if err != nil { panic(err) } fmt.Printf("block was reused: %t\n", &block[0] == &newBlock[0]) }
Output: free blocks: 0 total allocated: 1 resliced block is invalid: true free blocks: 1 total allocated: 1 block was reused: true
type PoolOpt ¶
type PoolOpt func(*poolOpts)
PoolOpt is a configuration option for a stealthpool
func WithBlockSize ¶
WithBlockSize specifies the block size that will be returned. It is highly advised that this block size be a multiple of 4KB or whatever value `os.Getpagesize()`, since the mmap syscall returns page aligned memory
func WithPreAlloc ¶
WithPreAlloc specifies how many blocks the pool should preallocate on initialization. Default is 0.