golanggc优化思路以及实例分析_第1页
golanggc优化思路以及实例分析_第2页
golanggc优化思路以及实例分析_第3页
golanggc优化思路以及实例分析_第4页
golanggc优化思路以及实例分析_第5页
已阅读5页,还剩1页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

golanggc优化思路以及实例分析⼀个即将上线的go写的⾼频服务,压测的时候发现gc特别⾼,⾼到10%-15%左右了,本⽂记录下优化gc的过程和和思路。线上环境1.10.⾸先,查看gc是否有异常,我们可以使⽤gctrace跟踪实时的gc。执⾏下⾯命令可以看到gc的实时信息。GODEBUG=gctrace=1goruncmd/agent_bin.go输出结果如下:gc45@37.801s11%:0.19+627+0.29msclock,0.38+424/621/0+0.59mscpu,356->415->225MB,453MBgoal,4Pgc46@39.126s11%:2.9+927+0.16msclock,5.8+342/925/0+0.33mscpu,361->460->275MB,450MBgoal,4Pgc47@40.847s12%:0.24+1096+0.12msclock,0.49+291/1007/0+0.24mscpu,427->559->319MB,551MBgoal,4Pgc48@42.771s12%:0.26+841+0.12msclock,0.52+377/830/0+0.24mscpu,486->561->271MB,638MBgoal,4Pgc49@44.429s12%:3.1+890+0.40msclock,6.2+492/833/0+0.81mscpu,440->528->294MB,543MBgoal,4Pgc50@46.188s12%:0.23+1165+0.13msclock,0.47+624/1158/0+0.27mscpu,471->579->323MB,589MBgoal,4Pgc51@48.252s13%:0.26+1410+0.14msclock,0.52+358/1336/9.9+0.28mscpu,506->620->343MB,646MBgoal,4Pgc52@50.942s13%:0.27+806+0.51msclock,0.55+403/805/200+1.0mscpu,549->657->340MB,687MBgoal,4Pgc53@53.014s13%:0.10+857+0.36msclock,0.21+467/851/94+0.73mscpu,546->666->351MB,681MBgoal,4Pgc45:表⽰第45次GC,共有4个P(线程)参与GC。11%:表⽰gc占时间⽐。0.19+627+0.29us:STW(stop-the-world)0.19ms,并发标记和扫描的时间627ms,STW标记的时间0.29ms。0.38+424/621/0+0.59mscpu,表⽰垃圾回收占⽤cpu时间356->415->225MB,453MBgoal,表⽰堆的⼤⼩,gc后堆的⼤⼩,存活堆的⼤⼩453MBgoal表⽰整体堆的⼤⼩为435M。根据官⽅描述,golang1.0的gc可以降到100ms以内,但是这⾥gc都超过1s了,这明显是不可以接受的,说明gc是有很⼤异常的。检查思路,⾸先利⽤pprof打出整个调⽤过程累计的堆分配图,查出到底是哪些模块堆分配异常。通过代码内嵌pprof暴露端⼝的⽅式,终端输出svg。import_"net/http/pprof"gofunc(){http.ListenAndServe(":8080",nil)}()然后终端输⼊:gotoolpprof-alloc_space-cum-svg:8080/debug/pprof/heap>heap.svg然后浏览器打开svg,找到消耗最⼤的调⽤栈:使⽤-cum,是累计函数调⽤栈的堆分配⼤⼩,因为图形会将调⽤栈很⼤的路线着重标识出来,这⾥可以明显看出是⾃⼰实现的Fetch函数的iocopyBuffer函数gc压⼒最⼤。这⾥⼤致知道来源了,那到底是我们函数哪个对象有问题?接下来我们对这个⽂件使⽤逃逸分析:直接⽤gobuild--gcflags=-m接⽂件即可:我们的源码是这样的:packagehttpclientimport("io""io/ioutil""net/http""net/url""os""sync""time""refresh_agent/utils")varclient*http.ClientvarbuffPoolsync.Poolfuncinit(){funcinit(){client=&http.Client{}http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost=2000http.DefaultTransport.(*http.Transport).MaxIdleConns=20000}typeHttpClientstruct{}funcNewHttpClient()*HttpClient{httpClient:=HttpClient{}return&httpClient}func(this*HttpClient)replaceUrl(srcUrlstring,ipstring)string{httpPrefix:="http://"parsedUrl,err:=url.Parse(srcUrl)iferr!=nil{return""}returnhttpPrefix+ip+parsedUrl.Path}func(this*HttpClient)downLoadFile(resp*http.Response)error{out,err:=os.OpenFile("/dev/null",os.O_RDWR|os.O_CREATE|os.O_APPEND,0666)deferout.Close()_,err=io.Copy(out,resp.Body)returnerr}func(this*HttpClient)Fetch(dstUrlstring,methodstring,proxyHoststring,headermap[string]string,preloadbool,timeoutint64)(*http.Response,error){//proxyHost换掉url中请求newUrl:=this.replaceUrl(dstUrl,proxyHost)req,_:=http.NewRequest(method,newUrl,nil)fork,v:=rangeheader{req.Header.Add(k,v)}req.Host=utils.GetUrlHost(dstUrl)client.Timeout=time.Duration(timeout)*time.Secondresp,err:=client.Do(req)ifresp==nil||err!=nil{returnresp,err}ifpreload{err:=this.downLoadFile(resp)iferr!=nil{returnnil,err}}io.Copy(ioutil.Discard,resp.Body)resp.Body.Close()returnresp,nil}逃逸分析的结果如下:gobuild--gcflags=-mlibs/httpclient/httpclient.go#refresh_agent/vendor//garyburd/redigo/redisgc1@0.078s5%:0.075+24+0.040msclock,0.15+0.15/11/17+0.081mscpu,16->16->11MB,42MBgoal,2P#refresh_agent/vendor//imroc/reqgc1@0.071s3%:0.017+26+0.050msclock,0.034+0.22/6.0/22+0.10mscpu,16->17->11MB,42MBgoal,2P#refresh_agent/vendor//BurntSushi/tomlgc1@0.054s6%:0.086+43+0.062msclock,0.17+0.14/12/14+0.12mscpu,16->17->12MB,42MBgoal,2P#refresh_agent/vendor//modern-go/reflect2gc1@0.054s7%:0.070+28+0.051msclock,0.14+0.15/12/14+0.10mscpu,16->17->10MB,42MBgoal,2Pgc2@0.334s3%:0.016+33+0.055msclock,0.032+0.54/12/20+0.11mscpu,37->38->14MB,54MBgoal,2P#refresh_agent/vendor//json-iterator/gogc1@0.045s5%:0.068+27+0.040msclock,0.13+2.5/4.7/16+0.081mscpu,16->16->11MB,42MBgoal,2Pgc2@0.211s9%:0.029+107+0.12msclock,0.058+0.96/52/0+0.24mscpu,39->44->26MB,58MBgoal,2P#command-line-argumentslibs/httpclient/httpclient.go:18:6:caninlineinit.0libs/httpclient/httpclient.go:26:6:caninlineNewHttpClientlibs/httpclient/httpclient.go:19:24:&http.Clientliteralescapestoheaplibs/httpclient/httpclient.go:28:9:&httpClientescapestoheaplibs/httpclient/httpclient.go:27:2:movedtoheap:httpClientlibs/httpclient/httpclient.go:31:62:leakingparam:srcUrllibs/httpclient/httpclient.go:37:25:httpPrefix+ip+parsedUrl.Pathescapestoheaplibs/httpclient/httpclient.go:31:62:(*HttpClient).replaceUrlthisdoesnotescapelibs/httpclient/httpclient.go:31:62:(*HttpClient).replaceUrlipdoesnotescapelibs/httpclient/httpclient.go:44:18:outescapestoheaplibs/httpclient/httpclient.go:44:28:resp.Bodyescapestoheaplibs/httpclient/httpclient.go:41:59:leakingparamcontent:resplibs/httpclient/httpclient.go:41:59:(*HttpClient).downLoadFilethisdoesnotescapelibs/httpclient/httpclient.go:48:151:leakingparam:dstUrllibs/httpclient/httpclient.go:48:151:leakingparam:methodlibs/httpclient/httpclient.go:48:151:leakingparamcontent:headerlibs/httpclient/httpclient.go:69:30:resp.Bodyescapestoheaplibs/httpclient/httpclient.go:48:151:(*HttpClient).Fetchthisdoesnotescapelibs/httpclient/httpclient.go:48:151:(*HttpClient).FetchproxyHostdoesnotescape<autogenerated>:1:0:leakingparam:.anon0<autogenerated>:1:0:leakingparam:.this<autogenerated>:1:0:leakingparam:io.p<autogenerated>:1:0:leakingparam:.this<autogenerated>:1:0:os.(*File).close.thisdoesnotescape<autogenerated>:1:0:leakingparam:.this<autogenerated>:1:0:leakingparam:.this<autogenerated>:1:0:leakingparam:.this<autogenerated>:1:0:leakingparam:context.key<autogenerated>:1:0:leakingparam:.this这⾥我们发现resp.Body逃逸到了堆上。和我们原始的希望是不⼀致的,我们的⽬的,是希望将resp.body重定向到空设备中去,这⾥的对象最好是分配到栈区。这⾥看不出,到底哪⼀步反复创建对象了。继续追io.CopyBuffer,查看io.Copy的源码:funcCopy(dstWriter,srcReader)(writtenint64,errerror){returncopyBuffer(dst,src,nil)}//CopyBufferisidenticaltoCopyexceptthatitstagesthroughthe//providedbuffer(ifoneisrequired)ratherthanallocatinga//temporaryone.Ifbufisnil,oneisallocated;otherwiseifithas//zerolength,CopyBufferpanics.funcCopyBuffer(dstWriter,srcReader,buf[]byte)(writtenint64,errerror){ifbuf!=nil&&len(buf)==0{panic("emptybufferinio.CopyBuffer")}returncopyBuffer(dst,src,buf)}funccopyBuffer(dstWriter,srcReader,buf[]byte)(writtenint64,errerror){//IfthereaderhasaWriteTomethod,useittodothecopy.//Avoidsanallocationandacopy.ifwt,ok:=src.(WriterTo);ok{returnwt.WriteTo(dst)}//Similarly,ifthewriterhasaReadFrommethod,useittodothecopy.ifrt,ok:=dst.(ReaderFrom);ok{returnrt.ReadFrom(src)}size:=32*1024ifl,ok:=src.(*LimitedReader);ok&&int64(size)>l.N{ifl.N<1{size=1}else{size=int(l.N)}}ifbuf==nil{buf=make([]byte,size)#这⾥反复创建⼤⼩为32k的slice}看到调⽤栈提⽰的函数,发现,我们调⽤copy的时候,如果不传⼊buffer,内部会反复创建这个buf。基本就定位到问题了。那么我们知道某个对象会⼀直被gc回收,怎么处理呢,通⽤做法是,使⽤对象池。经过

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论