Golang 1.8后本身自带了pprof的这个神器,可以帮助我们很方便的对服务做一个比较全面的profile。对于一个Golang的程序,可以从多个方面进行profile,比如memory和CPU两个最基本的指标,也是我们最关注的,同时对特有的goroutine也有运行状况profile。关于golang profiling本身就不做过多的说明,可以从 官方博客 中了解到详细的过程。 Profile的环境 Ubuntu 14.04.4 LTS (GNU/Linux 3.19.0-25-generic x86_64) go version go1.9.2 linux/amd64 profile的为一个elassticsearch数据导入接口,承担接受上游数据,根据元数据信息写入相应的es索引中。目前的状况是平均每秒是1.3Million的Doc数量。 在增加了profile后,从CPU层面发现几个问题。 runtime mallocgc 占用了17.96%的CPU。 SVG部分图如下 通过SVG图,可以看到调用链为: ioutil.ReadAll -> buffer.ReadFrom -> makeSlice -> malloc.go 然后进入ReadAll的源码。 readAll()方法 func readAll(r io.Reader, capacity int64) (b []byte, err error) { buf := bytes . NewBuffer ( make ([] byte , 0 , capacity )) // If the buffer overflows, we will get bytes.ErrTooLarge. // Return that as an error. Any other panic remains. defer func() { e := recover () if e == nil { return } if panicErr , ok := e .( error ) ; ok && panicErr == bytes . ErrTooLarg
回顾总结 process and thread process是操作系统调度的基本单位,一个程序可以理解为一个进程。 thread是process的子process,是process调度的基本单位。 从调度角度理解process和thread 我们知道操作系统调度的时候是以process为单位的,即当我们的程序是单thread的process时,那在这个process获得cpu的时间片时,这个thread能够获得全部的这个时间片。在内核中,我们维护着一个process的table,来记录每个process的基本信息,根据不同的调度方式来使用这个信息或者不使用。 当一个process拥有多个thread时,如何调度thread呢? 当thread为内核态thread时。 当thread为用户态thread时。 混合实现不做讨论 两种情况则有不同的调度方式。当为内核态的thread时,即此时的thread可以称之为轻量级的process,这个时候内核来维护一个thread table和process table相似。同时操作系统来调度thread,就像调度process一样。当为用户态的线程时,那么操作系统感受不到多个thread的存在,因为此时操作系统调度的对象是process,一个process中有多少个thread他不关心,所以当一个process得到CPU时间片后,他会自己来调度thread。 了解他们两个基本工作方式后,应该清楚这两种线程的主要区别:调度者是谁?各个的优缺点是什么?在JAVA中使用的哪种thread?Golang中goroutine是如何对应到我们操作系统的调度单位上去的? 调度者。 内核态的thread:内核。 用户态的thread:process 内核态thread的优点:在单个thread进行syscall而阻塞 时(一个线程不可以即运行代码,又能进行syscall),操作系统可以切换到同process中或者其他process下一个可以执行的thread,而不必阻塞,然而用户态的thread在进行syscall时,整个process下的thread都会阻塞。用户态的thread优点是在进行thread切换时消耗非常小,因为他不需要像内核态的thread那样需要陷入一次内核。