로그 트래픽을 udp에서 수신하고, 구문 분석을 시도한 다음 Redis에 삽입하는 프로그램이 있습니다. . 일정 수준의 트래픽에서, 메모리는골란 메모리 폭발 : newdefer
빠르게 ("폭발"기가 바이트에 수백 메가 바이트로 증가 할 것으로 보인다 나는 이런 일이 발생 직후 힙 프로파일을 잡고했고 다음과 같은 반환
(pprof) top100 -cum
Total: 1731.3 MB
0.0 0.0% 0.0% 1731.3 100.0% gosched0
1162.5 67.1% 67.1% 1162.5 67.1% newdefer
0.0 0.0% 67.1% 1162.5 67.1% runtime.deferproc
0.0 0.0% 67.1% 1162.0 67.1% main.TryParse
0.0 0.0% 67.1% 438.0 25.3% runtime.main
301.5 17.4% 84.6% 437.5 25.3% main.main
136.0 7.9% 92.4% 136.0 7.9% runtime.malg
0.0 0.0% 92.4% 136.0 7.9% runtime.newproc
0.0 0.0% 92.4% 136.0 7.9% runtime.newproc1
1.5 0.1% 92.5% 131.3 7.6% main.RedisCuller
0.0 0.0% 92.5% 108.5 6.3% github.com/garyburd/redigo/redis.(*conn).Do
0.0 0.0% 92.5% 108.5 6.3% github.com/garyburd/redigo/redis.(*conn).readReply
0.0 0.0% 92.5% 108.5 6.3% github.com/garyburd/redigo/redis.(*pooledConnection).Do
95.8 5.5% 98.0% 95.8 5.5% cnew
0.0 0.0% 98.0% 95.8 5.5% runtime.cnewarray
34.0 2.0% 100.0% 34.0 2.0% runtime.convT2E
0.0 0.0% 100.0% 0.5 0.0% main.init
0.0 0.0% 100.0% 0.5 0.0% net/http/pprof.init
0.0 0.0% 100.0% 0.5 0.0% sync.(*Once).Do
0.0 0.0% 100.0% 0.5 0.0% syscall.Getenv
0.0 0.0% 100.0% 0.5 0.0% time.init
프로그램이 경우
는 "건강한"프로필보다 다음과 같습니다
(pprof) top20 -cum
Total: 186.7 MB
0.0 0.0% 0.0% 186.7 100.0% gosched0
0.5 0.3% 0.3% 122.7 65.7% main.RedisCuller
0.0 0.0% 0.3% 103.5 55.4% github.com/garyburd/redigo/redis.(*pooledConnection).Do
0.0 0.0% 0.3% 103.0 55.2% github.com/garyburd/redigo/redis.(*conn).Do
0.0 0.0% 0.3% 103.0 55.2% github.com/garyburd/redigo/redis.(*conn).readReply
88.2 47.2% 47.5% 88.2 47.2% cnew
0.0 0.0% 47.5% 88.2 47.2% runtime.cnewarray
0.0 0.0% 47.5% 57.0 30.5% main.TryParse
57.0 30.5% 78.0% 57.0 30.5% newdefer
0.0 0.0% 78.0% 57.0 30.5% runtime.deferproc
34.0 18.2% 96.3% 34.0 18.2% runtime.convT2E
1.5 0.8% 97.1% 6.5 3.5% main.main
0.0 0.0% 97.1% 6.5 3.5% runtime.main
5.0 2.7% 99.7% 5.0 2.7% runtime.malg
0.0 0.0% 99.7% 5.0 2.7% runtime.newproc
0.0 0.0% 99.7% 5.0 2.7% runtime.newproc1
0.0 0.0% 99.7% 0.5 0.3% bufio.NewWriter
0.0 0.0% 99.7% 0.5 0.3% bufio.NewWriterSize
0.0 0.0% 99.7% 0.5 0.3% github.com/garyburd/redigo/redis.(*Pool).get
0.0 0.0% 99.7% 0.5 0.3% github.com/garyburd/redigo/redis.(*pooledConnection).get
나는 (이 자주 실패 할 수 있기 때문에) 파싱 기능을 둘러싼 내 코드에서 가진 유일한 연기 :
for {
rlen, _, err := sock.ReadFromUDP(buf[0:])
checkError(err)
raw := logrow.RawRecord(string(buf[:rlen]))
go TryParse(raw, c)
}
...
func TryParse(raw logrow.RawRecord, c chan logrow.Record) {
defer func() {
if r := recover(); r != nil {
//log.Printf("Failed Parse due to panic: %v", raw)
return
}
}()
rec, ok := logrow.ParseRawRecord(raw)
if !ok {
return
//log.Printf("Failed Parse: %v", raw)
} else {
c <- rec
}
}
사람이 갑자기 풍선을 일으킬 수있는 잘못된 일을하는 것은 명백합니까? 또는 그것을 잠그는 데 어떤 방향을 제시 할 수 있습니까?
편집합니다 (logrow.Record 채널의 주위에 더 많은 코드) : closured 지연 기능을 우리가 볼로 (이동 자체의 문제)의 누출로 밝혀졌다
c := make(chan logrow.Record)
...
go RedisInserter(c, bucket, retention, pool)
func RedisInserter(c chan logrow.Record, bucket, retention int, p *redis.Pool) {
for rec := range c {
logrow.SendToRedis(rec, bucket, retention, p)
}
}
당신은'c' ('chrow'의'logrow.Record')를 닫고 있습니까? 그렇다면 'c'의 종료를 연기 하시겠습니까? 더 많은 코드를 보지 않고 무엇을 제안해야할지 확신 할 수 없습니다. – Intermernet
@Intermernet : 해당 채널 주위에 조금 더 많은 코드를 추가했습니다. 내가 "닫아야 할"것은 일어나지 않아야한다. 왜냐하면 채널은'go TryParse'에 의해 끊임없이로 그로우로 가득 채워져 있고'RedisInserter' 루프는 그 행들을 소비해야만하기 때문입니다. 지금 그것을 보면 logrow.SendToRedis가 차단되어 채널이 채워질 수 있지만 높은 newdefer 메모리 사용으로 이어질 수는 없는지 알 수 있습니다. –
클로저 지연 기능에서 메모리 누수가 있습니다. 인쇄물의 주석을 제거한 경우 어떤 경우가 될 수 있습니다. : https://codereview.appspot.com/10784043/ 팁을 사용해 보셨습니까? – ClojureMostly