Why Go’s Memory Isn’t Returned to the OS Right Away – Understanding Delayed Release
This article examines Go’s memory management, explaining the distinction between RSS and VSZ, the runtime’s delayed memory return mechanism, common causes of memory spikes, and how to observe and force memory release using tools like ps and debug.FreeOSMemory(), illustrated with practical code examples.
Problem Introduction
Phenomenon : In production, Go services often trigger memory alerts when traffic increases or large files are processed; memory drops to about 100 MB and only recovers after a while.
Go’s memory management: the runtime holds freed memory and returns it to the OS only after a delay; premature return can hurt performance.
Memory leaks caused by CGO modules.
Excessive memory usage can crash the process, after which a supervisor restarts it and the memory curve normalizes.
Basic Concepts
RSS and VSZ Explained
RSS (Resident Set Size) is the amount of RAM actually used by a process, excluding swapped-out pages but including shared libraries, stack, and heap. VSZ (Virtual Memory Size) is the total virtual address space the process can access, including swap and shared memory.
Example : A process with 500 KB code, 2500 KB shared libraries, 200 KB stack (100 KB swapped) loads 1000 KB of libraries and 400 KB of its own code. RSS = 400 KB + 1000 KB + 100 KB = 1500 KB; VSZ = 500 KB + 2500 KB + 200 KB = 3200 KB.
Go Memory Management
Delayed Memory Return in Go Runtime
Go’s garbage collector automatically reclaims unreachable objects. Freed memory becomes “free” inside the runtime and can be reused, but is not immediately returned to the operating system. Typically, memory is returned only after about five minutes of inactivity.
For example, reading an HTTP request body into a bytes.Buffer does not release the memory to the OS after the request is processed; the memory is kept for reuse by subsequent requests.
Running a simple HTTP server and stress‑testing it with ab demonstrates this delayed return:
func main() {
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
http.ListenAndServe(":8010", nil)
fmt.Println("hello")
}Before load:
$ ps -p 563 -v
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
563 pts/19 SNl 0:27 0 2100 359203 3480 0.1 ./httpAfter stress test:
$ ps -p 563 -v
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
563 pts/19 SNl 0:27 0 2100 359203 12908 0.1 ./httpFive minutes later the RSS drops back:
$ ps -p 563 -v
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
563 pts/19 SNl 0:27 0 2100 359203 4816 0.0 ./httpThe RSS changes from 3480 KB to 12908 KB and back to 4816 KB, confirming Go’s delayed memory return behavior.
Forcing Memory Return
Calling debug.FreeOSMemory() forces the runtime to return as much memory as possible to the OS. Manual calls are generally discouraged because the runtime periodically performs this operation automatically.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
