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.
I created a minimal program that reproduces the issue. The following CGO-based code keeps allocating memory and does not free threads properly:
package mainI 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--
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...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/5e8c1622-e6f0-47a8-a869-e78797800fb5n%40googlegroups.com.
On Mar 4, 2025, at 1:26 PM, David Bell <davidbe...@gmail.com> wrote:
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/9a40383e-5b47-4f2b-bdea-a50946381650n%40googlegroups.com.
Hi Ian and Robert,
First, thank you both for taking the time to answer and provide insights! I really appreciate it.
To clarify, my real program is a long-lived process that receives network traffic from C++ code and processes it in Go.
The CGO calls in my program seemed to be the likely culprit behind the increasing memory usage I observed.
I tried adding runtime.LockOSThread() around the CGO calls and used a semaphore to limit concurrency.
This change has significantly improved the issue, as the memory growth is now much more controlled.
However, there’s something I still don’t fully understand. In my toy example, where I spawn 5000 CGO calls, locking the thread indeed causes Go to properly clean up the threads, and I end up with the correct number of threads. But despite that, memory usage still remains around 200MB.
If locking the thread ensures proper cleanup, why does memory usage remain high in the toy example, while in my real program, it seems to help? Is there some underlying mechanism in Go’s thread management or memory allocator that could explain this difference?
Thanks again for your help!
I wanted to add some clarification on my misunderstanding and the approaches I tried.
I tested two different strategies to handle the CGO calls:
Worker Pool Approach (50 workers handling CGO calls)
Locking OS Threads with a Semaphore (Up to 500 concurrent CGO calls)
What I don’t quite understand is why the second approach works while the first one doesn’t.
If the worker pool limits concurrency to 50 workers, why does it still cause memory growth, while 500 locked threads behave better? Is it because of how Go manages OS threads internally?
Appreciate your thoughts on this!
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/82290274-54d4-41c5-8c9f-4d881e6b474dn%40googlegroups.com.