*Hi everyone,*

I'm relatively new to Go and even newer to *CGO*, so I’d really appreciate 
any guidance on an issue I’ve been facing.

*Problem Overview*

I noticed that when using CGO, my application's *memory usage keeps 
increasing*, and threads do not seem to be properly cleaned up. 
The Go runtime (pprof) does not indicate any leaks, but when I monitor the 
process using *Activity Monitor*, I see a growing number of threads and 
increasing memory consumption.
*Observations* 
   
   - Memory usage *keeps increasing* over time when using CGO, even after 
   forcing garbage collection. 
   - Threads spawned for CGO calls appear to *not be released*, causing a 
   large number of lingering threads. 
   - The issue is *not present* when using pure Go time.Sleep() instead of 
   the CGO function. 



*Reproducible Example* 

I created a minimal program that reproduces the issue. The following 
*CGO-based* code keeps allocating memory and does not free threads properly:
package main

import (
"fmt"
"runtime"
"runtime/debug"
"sync"
"time"
)

/*
#include <unistd.h>
void cgoSleep() {
  sleep(1);
}
*/
import "C"

func main() {
start := time.Now()

var wg sync.WaitGroup
for i := 0; i < 5000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
C.cgoSleep()
}()
}
wg.Wait()

end := time.Now()

// Force GC and free OS memory
runtime.GC()
debug.FreeOSMemory()
time.Sleep(10 * time.Second)

var m runtime.MemStats
runtime.ReadMemStats(&m)

fmt.Printf("Alloc = %v MiB", m.Alloc/1024/1024)
fmt.Printf("\tTotalAlloc = %v MiB", m.TotalAlloc/1024/1024)
fmt.Printf("\tSys = %v MiB", m.Sys/1024/1024)
fmt.Printf("\tNumGC = %v\n", m.NumGC)
fmt.Printf("Total time: %v\n", end.Sub(start))

select {}
}

*Expected Behavior* 
   
   - The memory usage should *not* continue rising indefinitely. 
   - Threads should be properly cleaned up when they finish executing. 
   - The behavior should be similar to the following *pure Go* equivalent, 
   which does *not* exhibit the issue: 



*Actual Results* *With CGO (cgoSleep()):* 
   
   - *Memory Usage:* *296 MB* 
   - *Threads:* *5,003* 
   - *System Memory (Sys from runtime.MemStats)*: 


*205 MB * 

*With Pure Go (time.Sleep()):* 
   
   - *Memory Usage:* *14 MB* 
   - *Threads:* *14* 
   - *System Memory (Sys from runtime.MemStats)*: *24 MB* 

*Additional Attempt* 

I tried forcing thread cleanup using runtime.LockOSThread() and 
runtime.Goexit(), but *while the number of threads decreases, memory is 
still never fully released*:
go func() { runtime.LockOSThread() defer wg.Done() C.cgoSleep() 
runtime.Goexit() }() *Questions* 
   
   1. *Why is memory increasing indefinitely with CGO?* 
   2. *Why are threads not getting properly cleaned up after CGO calls?* 
   3. *Is there a way to force the Go runtime to reclaim memory allocated 
   for CGO threads?* 
   4. *Is there a better approach to handling CGO calls that spawn 
   short-lived threads?* 
   5. *Would using runtime.UnlockOSThread() help in this case, or is this 
   purely a CGO threading issue?* 
   6. *Is there a way to track down where the memory is being held? Since 
   pprof does not show high memory usage, what other tools can I use?* 

*Go Version* 
go1.23.5 darwin/arm64

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/golang-nuts/5e8c1622-e6f0-47a8-a869-e78797800fb5n%40googlegroups.com.

Reply via email to