Skip to content

fmt: incorrect usage of sync.Pool #27740

@dsnet

Description

@dsnet

Consider this snippet:

type CustomStringer int

func (c CustomStringer) String() string {
	time.Sleep(500 * time.Millisecond) // simulate processing time
	return string(make([]byte, int(c)))
}

func main() {
	processRequest := func(size int) {
		fmt.Sprintf("<%v>", CustomStringer(size))
		time.Sleep(1 * time.Millisecond) // simulate idle time
	}

	// Simulate a steady stream of infrequent large requests.
	go func() {
		for {
			processRequest(1 << 28) // 256MiB
		}
	}()

	// Simulate a storm of small requests.
	for i := 0; i < 1000; i++ {
		go func() {
			for {
				processRequest(1 << 10) // 1KiB
			}
		}()
	}

	// Continually run a GC and track the allocated bytes.
	var stats runtime.MemStats
	for i := 0; ; i++ {
		runtime.ReadMemStats(&stats)
		fmt.Printf("Cycle %d: %dB\n", i, stats.Alloc)
		time.Sleep(time.Second)
		runtime.GC()
	}
}

This is a variation of #23199 (comment). See that comment for why this memory leak occurs.

After the 50th GC cycle, this pins 7GiB of heap, when I expect it to use no more than 256MiB.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions