dnsproxy icon indicating copy to clipboard operation
dnsproxy copied to clipboard

关于内存泄露问题

Open alpc40 opened this issue 6 years ago • 1 comments

感谢作者的奉献,我在使用过程中发现一些内存的问题,实测如下

线程过多

github.com/miekg/dns/server.go

for srv.isStarted() {
		m, s, err := reader.ReadUDP(l, rtimeout)
		if err != nil {
			if !srv.isStarted() {
				return nil
			}
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				continue
			}
			return err
		}
		if len(m) < headerSize {
			if cap(m) == srv.UDPSize {
				srv.udpPool.Put(m[:srv.UDPSize])
			}
			continue
		}
		wg.Add(1)
		go srv.serveUDPPacket(&wg, m, l, s)
	}

我在windows使用此代理,然后用200个线程去nslookup,发现线程数不断增加,貌似在"github.com/miekg/dns"库中,并没有对UDP线程数进行限制,所以需要自己写一个UDP线程数限制的server,而不能直接拿过来用。

内存泄露

libdns_utils.go

func (dt *DnsTransPort) legallySpawnExchange(req *dns.Msg) (*dns.Msg, error) {
	const spawnNum int8 = 3
	resp := make(chan *dns.Msg, spawnNum)
	lastErr := make(chan error)
	var failedTimes int32

	for range [spawnNum]struct{}{} {
		go func() {
			if r, err := dt.Exchange(req); err == nil {
				resp <- r
			} else {
				if atomic.LoadInt32(&failedTimes) == int32(spawnNum-1) {
					resp <- nil
					lastErr <- err
				} else {
					atomic.AddInt32(&failedTimes, 1)
				}
			}
		}()
	}

	if r := <-resp; r != nil {
		return r, nil
	} else {
		return nil, <-lastErr
	}
}

在libdns_utils.go文件的legallySpawnExchange文件中,起了3个线程发给同一个server,在线程比较多的情况下(我用的200个),其中1个线程返回,另外2个线程可能因为争抢资源而一直存在,导致goroutine一直增加,建议还是不要增加线程,直接在主线程中跑

alpc40 avatar Jul 15 '19 13:07 alpc40

以上200线程有点夸张,但我实际部署中确实存在个别机器暴内存的现象,原因可以是电脑负载较重或本身资源有限,使用200线程可以在任意电脑复现

alpc40 avatar Jul 15 '19 13:07 alpc40