BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / golang / #763同步于 2017/2/19
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Golang机器人发帖

请大家帮忙看一个内存泄露的问题

asm
2017/2/19镜像同步2 回复
我的一个线上程序,出现内存泄露问题,运行3-5天后内存居高不下,远超出该程序本应该占用的内存,我把出问题的这段代码贴出来,请大家帮忙看一下问题所在 ```Go package main import ( "io" "io/ioutil" "net" "net/http" _ "net/http/pprof" "net/url" "os" "runtime" "strconv" "strings" "sync" "time" ) func run(addr string) { ln, err := reuse.Listen("tcp", addr) if err != nil { log.Error("error listening %v, %v\n", addr, err) return } log.Info("server listening at %v ...\n", addr) for { conn, err := ln.Accept() if err != nil { log.Error("accept error: %v\n", err) return } go handleConnection(conn) } } type GateServerConn struct { appId string sessionId string closed bool gateChan chan *util.LeakyBuf sessionChan chan *util.LeakyBuf keepAliveChan chan *util.LeakyBuf sessionClientChanSize int bandWidth uint16 } func handleConnection(conn net.Conn) { /* 省略部分代码 */ gateServerConn = &GateServerConn{ appId: appId, sessionId: sessionId, closed: false, /*第55行*/ gateChan: make(chan *util.LeakyBuf, common.GateChanSize), keepAliveChan: make(chan *util.LeakyBuf, 100), sessionClientChanSize: 0, bandWidth: uint16(bandWidth * 10), } gateServerConnMapMutex.Lock() gateServerConnMap[sessionId] = gateServerConn gateServerConnMapMutex.Unlock() sessionChanMapMutex.RLock() sessionChan, ok := sessionChanMap[sessionAddr] sessionChanMapMutex.RUnlock() if !ok { sessionChanMapMutex.Lock() /*第68行*/ sessionChan = make(chan *util.LeakyBuf, common.SessionChanSize) sessionChanMap[sessionAddr] = sessionChan sessionChanMapMutex.Unlock() go startSessionProxy(sessionAddr, sessionChan) } gateServerConn.sessionChan = sessionChan var wg sync.WaitGroup wg.Add(1) go func() { defer func() { if err := recover(); err != nil { log.Error("%v %s->%s:gate server recv failed:%v\n", gateServerConn.sessionId, conn.RemoteAddr(), conn.LocalAddr(), err) } }() defer func() { gateServerConn.closed = true conn.Close() util.SafeClose(gateServerConn.gateChan) util.SafeClose(gateServerConn.keepAliveChan) wg.Done() }() readTimeout := time.Duration(common.GateServerReadTimeout) * time.Second var buf *util.LeakyBuf for !gateServerConn.closed { // 省略业务逻辑代码 } }() wg.Wait() closeSession(session_id) } func startSessionProxy(session_server_addr string, sessionChan chan *util.LeakyBuf) { // 省略业务逻辑代码 } var config util.Config var log util.Logger var leakyBufPool = util.NewLeakyBufPool(util.MaxNBuf, util.LeakyBufSize) var gateServerConnMap map[string]*GateServerConn var sessionChanMap map[string]chan *util.LeakyBuf var gateServerConnMapMutex *sync.RWMutex var sessionChanMapMutex *sync.RWMutex var whiteIPList string func main() { // 省略部分代码 go run(serverAddr) util.WaitSignal() } ``` 在程序运行一段时间后,我使用go pprof查看goroutine和heap使用情况,发现heap异常: heap profile: 15926: 918443936 [252871: 990891280] @ heap/1048576 15393: 630497280 [15559: 637296640] @ 0x40f4f1 0x4054b3 0x403a95 0x401dc3 0x468961 0x4054b2 main.createSessionWithUrl+0x1992 0x403a94 main.createSession+0x1a4 0x401dc2 main.handleConnection+0x9d2 35: 280125440 [35: 280125440] @ 0x40f4f1 0x40574e 0x403a95 0x401dc3 0x468961 0x40574d main.createSessionWithUrl+0x1c2d 0x403a94 main.createSession+0x1a4 0x401dc2 main.handleConnection+0x9d2 这是heap的使用情况,可以看到这两个heap占用内存比较大,第一个heap有15393个对象,对应代码第55行,第2个heap有35个对象,对应代码第68行。 然后我查看goroutine情况,发现确实有35个startSessionProxy的goroutine,这符合我们的期望, 但是handleConnection这个goroutine只有694个,按道理一个handleConnection的goroutine应该对应一个gateServerConn对象, 也就是一个gateServerConn.gateChan对象,而我们看heap信息得到的gateChan数量是15393, 远远对不上,数毫无疑问是有内存泄露存在,但是我没有找到什么地方存在泄露,请大家帮忙看看!
订阅后,新回复会通过你的通知中心匿名送达。
2 条回复
wanghaohebe机器人#1 · 2017/2/19
gateServerConn.closed这个永远都是false?
asm机器人#2 · 2017/2/20
不是,遇到错误会改成true,这段被我省略掉了 问题已经找到了,是closeSession(session_id)中没有正确清理gateServerMap导致gateServerConn的引用一直存在 【 在 wanghaohebe 的大作中提到: 】 : gateServerConn.closed这个永远都是false?