分析goroutine是否泄漏
  • 从pprof的goroutine分析,是否是goroutine在持续增长。如果持续增长。goroutine肯定泄漏

package main

import (
"net/http"
_ "net/http/pprof"
"time"
)

type none struct{}

func main() {
	go func() {
		ch := make(chan none)
		consumer(ch)
		producer(ch)
	}()
	_ = http.ListenAndServe("0.0.0.0:8080", nil)
}

func consumer(ch chan none) {
	for i := 0; i < 1000; i++ {
		// 此处类似协程泄漏
		go func() {
			<-ch
		}()

		time.Sleep(3 * time.Microsecond)
	}
}

func producer(ch chan none) {
	time.Sleep(100 * time.Second)
	for i := 0; i < 1000; i++ {
		ch <- none{}

	}
}

上述代码中,逐步创建了1k个goroutine(假定是泄漏的),我们可以通过http://127.0.0.1:8080/debug/pprof/ 访问查看goroutine的变化情况。

  1. 在debug 中观察goroutine的数量变化,如果持续增长,那可以确定是goroutine 泄漏了。
    在这里插入图片描述
  2. 之后访问 http://127.0.0.1:8080/debug/pprof/goroutine?debug=1查看各goroutine数量,查看持续增加的goroutine ,如果存在持续增长的goroutine,那从goroutine的堆栈代码短分析即可。
    在这里插入图片描述
数据泄漏
  • 数据泄漏出现的问题比较多。比如长的string,slice 数据用切片的方式被引用,如果切片后的数据不释放,长的string,slice 是不会释放的,当然这种泄漏比较小
package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	_ "net/http/pprof"
	"time"
)

type None int64

func main() {
	go func() {
		singals := []int64{}
		netListen, _ := net.Listen("tcp", ":30000")
		defer netListen.Close()

		for {
			conn, err := netListen.Accept()
			if err != nil {
				fmt.Println("Accept Error")
			}

			singals = append(singals, 1)

			go doSomething(conn)
		}

		for _ = range singals {
			fmt.Println("Received")
		}
	}()

	_ = http.ListenAndServe("0.0.0.0:8080", nil)
}

func doSomething(conn net.Conn) {
	defer conn.Close()
	time.Sleep(100 * time.Microsecond)
	buf, err := ioutil.ReadAll(conn)
	if err == nil {
		fmt.Println(string(buf))
	}
}

例子比较简单,从net Accept 数据,并开启一个goroutine 做数据处理。singals 呢,用于做事件处理,每接收一个链接,给singal 推一条数据。
为了从中查找内存泄漏,我们也增加了pprof。
为了能尽快发现问题,我这边用了一个简单的shell对服务施压(请求2w http 服务,不关心请求返回结果)。命令如下:

seq 0 20000

从pprof 的 heap 中,我们能轻易的发现:
在这里插入图片描述
内存分配中,mem_leak文件的26行(append) 操作 申请的内存排在了top 1,仔细看代码,发现我们slice中的数据从来没有释放,所以造成了上面的问题。
如何解决这个问题呢?其实比较简单。只需要将slice,修改成带cache的chan(作为一个队列来使用),当数据使用过后即可销毁。不仅不会再出现内存泄漏,也保证了功能上的一致性。(当然需要重新起一个协程, 由于上面的for 是阻塞的,不会断开,所以也导致了下面的slice 不工作)

http://www.360doc.com/showweb/0/0/936552813.aspx