IT技术博客大学习 共学习 共进步

记一次内存泄露的debug过程

也就这样, 2016-03-16 23:42:05 浏览 2,303 次

   在压测 代码在线运行 工具的时候,发现当并发比较高的时候程序占用的内存会飙升,而且在中断压测之后,内存占用并没有回落。

   第一个能想到的办法就是去看代码,但是大多数时候,自己写的代码,很难review出太多的问题;于是就借助golang的pprof来定位问题。

在程序中嵌入 pprof

package main

import (

   "tool.lu/sandbox-server/app"

   "net/http"

   _ "net/http/pprof"

   "strconv"

   "runtime"

)

func main() {

   debug()

   server := app.NewApp()

   server.Run(":9090")

}

func debug() {

   go func() {

       // 这边是由于通过pprof发现问题之后,加的一段debug代码;后面会讲到

       http.HandleFunc("/go", func(w http.ResponseWriter, r *http.Request) {

           num := strconv.FormatInt(int64(runtime.NumGoroutine()), 10)

           w.Write([]byte(num))

       })

       http.ListenAndServe("localhost:6060", nil)

   }()

}

   通过 go tool 工具,查看内存分配最多的 top 5

go tool pprof http://localhost:6060/debug/pprof/heap

top 5

   Screenshot 2016-02-07 at 17.55.39.png

   查看代码,发现是 goroutine, ioPipe 的问题,一定是使用姿势出了问题:

   Screenshot 2016-02-07 at 17.56.40.png

   于是便有了上面的那段代码,curl http://localhost:6060/go,查看当前 go routine 的数量;于是猜测是因为 ioPipe 没有正确的关闭,引起 go routine 大量的产生,但是没有退出,耗费大量的内存;于是在异常退出前,主动关闭 ioPipe 的Reader,至此问题解决。

压测验证

   本机

wrk -t5 -c20 -d10000s -s post.lua http://tool.lu

   服务器

   Screenshot 2016-02-07 at 16.24.24.png

curl http://localhost:6060/go

总结

   这是一个很小的bug,由于写代码的时候不仔细,return之前没有关闭资源造成,但却要花费不少的力气去解决;对语言自己提供的工具链需要熟悉在熟悉,这样不管在解决问题或者避免问题的时候,都能节省很多的时间。

建议继续学习

  1. 近期Imgsrc一处内存泄露问题的查找和解决 (阅读 6,662)
  2. 了解前端内存泄露 (阅读 5,481)
  3. GLIBC内存分配机制引发的“内存泄露” (阅读 5,102)
  4. GC与JS内存泄露 (阅读 4,604)
  5. 一个 Lua 内存泄露检查工具 (阅读 3,884)
  6. PHP内存耗尽错误分析 (阅读 3,082)
  7. Golang socket 里面奇怪的 pipe 使用 (阅读 2,443)
  8. Android应用内存泄露分析、改善经验总结 (阅读 2,383)