gocron icon indicating copy to clipboard operation
gocron copied to clipboard

fix:任务超时导致内存泄露

Open sir997 opened this issue 4 years ago • 5 comments
trafficstars

sir997 avatar Apr 13 '21 09:04 sir997

没看出这段代码哪里导致内存泄露了,要怎么模拟内存泄露。

// 用这种方式kill, 子进程不会退出。

if err := cmd.Process.Kill(); err != nil {
	logger.Errorf("Process kill pid:%d err:%v", cmd.Process.Pid, err)
}

ouqiang avatar Apr 14 '21 01:04 ouqiang

如果任务超时,会执行 ctx.Done(),然后退出 ExecShell 函数同时结束resultChan接收,resultChan是没有缓冲的,会导致协程中写入resultChan一直阻塞。 复现步骤,构建一个运行时间稍微久的任务,并设置任务超时时间,到达超时时间后即可出现。 定位问题用的pprof,执行 go tool pprof http://localhost:6789/debug/pprof/goroutine,有协程阻塞在 output, err := cmd.CombinedOutput(),这个是正常的,还有的阻塞在 resultChan <- Result{string(output), err},这个就明显有问题了。

syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL),这个不太清楚为什么用这个,没有用cmd.Process.Kill(),我在mac上用第一种方式无法kill,第二种可以。

ExecShell返回的数据我觉得需要及时返回,CombinedOutput() 会导致运行时间久或输出多的任务占用很大内存

在 2021年4月14日 @.***> 写道:

没看出这段代码哪里导致内存泄露了,要怎么模拟内存泄露。 // 用这种方式kill, 子进程不会退出。 if err := cmd.Process.Kill(); err != nil { logger.Errorf("Process kill pid:%d err:%v", cmd.Process.Pid, err) } — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

sir997 avatar Apr 14 '21 03:04 sir997

任务超时后在ctx.Done()里面就return了,不会有机会执行到这里了

在 2021年4月14日 @.***> 写道:

这里读取了chan,不会造成阻塞 case result := <-resultChan: return result.output, result.err } — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

sir997 avatar Apr 14 '21 05:04 sir997

写了个demo

// 模拟任务执行,任务耗时3s,超时时间1s, func main() { cmd := exec.Command("/bin/bash", "-c", "sleep 3") ctx, _ := context.WithTimeout(context.Background(), time.Second)

ch := make(chan string) go func() { bs, err := cmd.CombinedOutput() fmt.Println("bs", string(bs), "err", err) // 任务超时这里会一直阻塞 ch <- string(bs) fmt.Println("routine down!") }()

select { case str := <-ch: fmt.Println("receive:", str) case <-ctx.Done(): cmd.Process.Kill() fmt.Println("timeout") }

time.Sleep(time.Second * 10) }

在 2021年4月14日 @.***> 写道:

这里读取了chan,不会造成阻塞 case result := <-resultChan: return result.output, result.err } — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

sir997 avatar Apr 14 '21 05:04 sir997

的确存在内存泄露的情况,这块代码要重写,可能也会出现竞态条件,并发读写cmd.Process。

这段代码的作用是kill掉所有子进程,比如A创建了子进程B, B创建了子进程C。 cmd.Process.Kill()只对A有效,B和C进程仍会继续运行。

syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)

ouqiang avatar Apr 14 '21 06:04 ouqiang