【发布时间】:2016-03-24 14:22:14
【问题描述】:
我遇到了一个情况,一个 go 程序占用了 15gig 的虚拟内存并继续增长。这个问题只发生在我们的 CentOS 服务器上。在我的 OSX 开发机器上,我无法重现它。
我是否在 go 中发现了一个错误,或者我做错了什么?
我已将问题归结为一个简单的演示,现在我将对其进行描述。首先构建并运行这个 go 服务器:
package main
import (
"net/http"
"os/exec"
)
func main() {
http.HandleFunc("/startapp", startAppHandler)
http.ListenAndServe(":8081", nil)
}
func startCmd() {
cmd := exec.Command("/tmp/sleepscript.sh")
cmd.Start()
cmd.Wait()
}
func startAppHandler(w http.ResponseWriter, r *http.Request) {
startCmd()
w.Write([]byte("Done"))
}
创建一个名为 /tmp/sleepscript.sh 的文件并将其更改为 755
#!/bin/bash
sleep 5
然后向 /startapp 发出多个并发请求。在 bash shell 中,您可以这样做:
for i in {1..300}; do (curl http://localhost:8081/startapp &); done
VIRT 内存现在应该是几个 GB。如果重新运行上面的 for 循环,VIRT 内存每次都会继续增长千兆字节。
更新 1: 问题是我在 CentOS 上遇到了 OOM 问题。 (感谢@nos)
更新 2: 通过使用 daemonize 并将调用同步到 Cmd.Run() 解决了该问题。感谢@JimB 确认在它自己的线程中运行的.Wait() 是POSIX api 的一部分,并且没有办法避免在不泄漏资源的情况下调用.Wait()。
【问题讨论】:
-
VirtualMemory,尤其是在 OSX 上,毫无意义。 RSS 可以更好地指导进程占用多少内存。
-
提示:Run() 执行进程并挂起直到退出。
-
我还建议分配一个pool of goroutines 来限制同时运行的进程数。
-
@Zippoxer 你的分数很受欢迎,但是 Run 也遇到了同样的虚拟内存爆炸。 Run 只是 .Start() 后跟 .Wait() 的简写。对于许多项目来说,goroutines 池是一个很好的架构选择,但在这种情况下并不是我真正想要的。
-
@Gattster 我建议您使用此信息更新帖子。虚拟内存增长到数 GB 本身并没有任何问题。即使我们人类不喜欢在
ps或top中看到如此庞大的数字,也可以抓取大量可能仍未使用的虚拟内存。你最终会遇到 OOM,至少如果情况是这样的话您确实要等到所有 300 个请求都完成并且 shell 脚本终止,然后再触发另一个 300 个请求的测试是一个问题。
标签: linux go memory-leaks