gpt4 book ai didi

linux - 执行并发 os/exec.Command.Wait() 时发生内存泄漏

转载 作者:IT王子 更新时间:2023-10-29 01:42:24 25 4
gpt4 key购买 nike

我遇到了这样一种情况,一个 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 内存现在应该有几千兆字节。如果重新运行上面的 for 循环,VIRT 内存每次都会继续增长 GB。

更新 1: 问题是我在 CentOS 上遇到了 OOM 问题。 (感谢@nos)

更新 2:通过使用 daemonize 并将调用同步到 Cmd.Run() 解决了该问题。感谢@JimB 确认在它自己的线程中运行的 .Wait() 是 POSIX api 的一部分,并且没有办法避免调用 .Wait()泄漏资源。

最佳答案

您发出的每个请求都需要 Go 生成一个新的操作系统线程以在子进程上 Wait。每个线程将消耗 2MB 堆栈和更大的 VIRT 内存块(这不太相关,因为它是虚拟的,但您可能仍会遇到 ulimit 设置)。线程被 Go 运行时重用,但它们目前从未被销毁,因为大多数使用大量线程的程序都会再次这样做。

如果您同时发出 300 个请求,并在发出任何其他请求之前等待它们完成,内存应该会稳定下来。但是,如果您在其他请求完成之前继续发送更多请求,您将耗尽一些系统资源:内存、文件描述符或线程。

关键是生成子进程并调用 wait 不是免费的,如果这是一个真实世界的用例,您需要限制 startCmd( ) 可以并发调用。

关于linux - 执行并发 os/exec.Command.Wait() 时发生内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34346064/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com