- run the main.go using
go run main.go
- find the pid of the process and send a USR1 signal using
kill -USR1 pid-of-main
- the
heap_profile.pprof
will be generated in the directory themain
process was running - run
go tool pprof -http=:8081 heap_profile.pprof
to visualize the heap
Created
January 14, 2025 15:13
-
-
Save dims/458b49e11f6790c30a8255f48efb952c to your computer and use it in GitHub Desktop.
Using pprof to find memory leaks for golang binaries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment