package main import ( "fmt" "math/rand" "os" "os/signal" "runtime/pprof" "sync" "syscall" "time" ) func setupSignalHandlerForHeapProfile() { // Set up signal handler sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGUSR1) go func() { for { <-sigChan // Generate heap profile when SIGUSR1 is received f, _ := os.Create("heap_profile.pprof") pprof.WriteHeapProfile(f) f.Close() } }() } func main() { setupSignalHandlerForHeapProfile() cache := &Cache{ items: make(map[string]*Resource), } var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go leakyGoroutine(&wg, cache) } ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() go func() { for range ticker.C { fmt.Printf("Cache size: %d\n", len(cache.items)) } }() wg.Wait() // This will never complete } type Resource struct { data []byte } type Cache struct { mu sync.Mutex items map[string]*Resource } func (c *Cache) Set(key string, value *Resource) { c.mu.Lock() defer c.mu.Unlock() c.items[key] = value } func (c *Cache) Get(key string) *Resource { c.mu.Lock() defer c.mu.Unlock() return c.items[key] } func leakyGoroutine(wg *sync.WaitGroup, cache *Cache) { defer wg.Done() for { key := fmt.Sprintf("key-%d", rand.Intn(1000)) resource := &Resource{ data: make([]byte, 1024*1024), // Allocate 1MB } cache.Set(key, resource) time.Sleep(time.Millisecond * 100) } }