The Go garbage collector is more or less designed to let your program
get up to its working set. When the GC runs when you have N bytes
allocated, it sets its next run to occur when you have N*2 bytes
allocated. In your case, the GC can essentially never free anything.
Each time it tries, the finalizer brings the value back to life by
storing it in the pool. So the GC keeps increasing the points at
which it will run, until your program runs out of memory.
This is not a good way to use sync.Pool, not only because it doesn't
work, but because the whole point of sync.Pool is to take work away
from the GC. When you use a finalizer to add a value back to the
pool, you are missing the point of the pool. The GC is doing the work
it requires to find that the value can be freed and then run the
finalizer. The expensive part of GC is not the allocation; it's
finding the free memory. Using a finalizer to add a value back to
sync.Pool forces the GC to do the expensive work in order to save on
the cheap work. It doesn't make sense.
Ian