Golang中的CPU占满100%及解决方案

OctopusLian · · 5575 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
有一个流媒体适配服务,出现了`CPU`开销很大的问题,一个服务把`CPU`资源占满了,导致其他服务无法正常工作。 下面来详细记录发现`bug`和解决的流程。 # 发现CPU开销很大 扫描发现,是垃圾回收导致 `CPU` 使用上升 : ``` Time: Mar 22, 2019 at 5:52pm (CST) Duration: 1mins, Total samples = 1.43mins (142.57%) Entering interactive mode (type "help" for commands, "o" for options) (pprof) tree Showing nodes accounting for 83.13s, 97.11% of 85.60s total Dropped 256 nodes (cum <= 0.43s) ----------------------------------------------------------+------------- flat flat% sum% cum cum% calls calls% + context ----------------------------------------------------------+------------- 71.36s 99.86% | runtime.gcDrain 0.10s 0.14% | runtime.systemstack 48.56s 56.73% 56.73% 71.46s 83.48% | runtime.scanobject 11.86s 16.60% | runtime.heapBitsForObject 11.04s 15.45% | runtime.greyobject ----------------------------------------------------------+------------- 11.86s 99.92% | runtime.scanobject 11.87s 13.87% 70.60% 11.87s 13.87% | runtime.heapBitsForObject ----------------------------------------------------------+------------- 11.04s 100% | runtime.scanobject 11.02s 12.87% 83.47% 11.04s 12.90% | runtime.greyobject ----------------------------------------------------------+------------- 6.53s 95.05% | runtime.gosweepone.func1 0.34s 4.95% | runtime.(*mheap).alloc 4.34s 5.07% 88.54% 6.87s 8.03% | runtime.sweepone 2.53s 36.83% | runtime.(*mspan).sweep ----------------------------------------------------------+------------- 74.42s 100% | runtime.gcBgMarkWorker.func2 1.97s 2.30% 90.84% 74.42s 86.94% | runtime.gcDrain 71.36s 95.89% | runtime.scanobject 0.52s 0.7% | runtime.pollWork ----------------------------------------------------------+------------- ``` # 准备工作 之后考虑使用 `buffer pool`, ```go // 这里不再分配新的内存,而是从 buffer pool 里面 GET databuf = make([]byte, 100000) ``` # 解决 参考[go buffer pool](https://github.com/libp2p/go-buffer-pool) - 先创建一个`buffer pool` - 在`Get`它 - 用完再`Put`回去 - 注意,最好在`Get`和`Put`时加锁。 # 是什么原因导致了CPU开销很大(重点) 当我们新建了一个有长度变量时,例如100`byte`的数组,那么它在操作系统内存中是这样展现的 ![memory.png](https://static.studygolang.com/190527/a63e72ba599053c9a3a483828a2b0365.png) 因此,当我们新建一个变量时,操作系统会在自己的运行内存里开辟一块内存给这个变量存数据用。当我们不需要这个数据时,或者说要删除这个变量时,`Golang`会执行垃圾回收机制。 然而当`Golang`在执行垃圾回收时,操作系统会不断对这些有或者没有被引用的变量进行扫描,这中间涉及操作系统的算法,我们不用深究,但是,在执行这种算法时,会占用`CPU`的资源,如果新开辟的变量和内存过多,就会导致系统不停的检查是否有不需要引用的变量了,从而造成占用`CPU`资源过多。 ## 解决办法 创建一个`buffer pool` 创建一个大的`buffer pool`,你需要内存时,向`buffer pool`获取一下`Get`,用完不需要时再还回去`Put`。 ![bufferpool.png](https://static.studygolang.com/190527/5975ef8fd863cf1e5f0e2aa29156e499.png) 这样做的好处是,操作系统每次检查内存时,都只有一个`buffer pool`在引用,不增不减,于是也就减少`CPU`资源的消耗了。 ## 打个比方 比如说操作系统就是一个土豪,借东西再换回来不收利息。它有一个很大的内存,周围许多人都想找它去借(新声明的变量并初始化),刚开始借的人只有十几个,后面有上万个,于是它要每天记录谁借了多少内存出去,谁还没有归还,归还的直接从记录上把名字划掉(垃圾回收)。后来操作系统烦了,于是就建了一个很大的内存池,够所有人分批次借,只要借完及时归还就行,而它每次去看这个内存池有没有变小即可,省了不少精力。 而这个内存池就是`go buffer pool`的作用。 ## 注意 建议给`Get`和`Put`加锁,防止多个协程同时借阅,造成竞争冒险。 这个`CPU`占满问题涉及`Golang`的垃圾回收机制,这块是要点,一定要搞明白。 # 参考文章和资源 - [go buffer pool](https://github.com/libp2p/go-buffer-pool) - [Golang 垃圾回收剖析](http://legendtkl.com/2017/04/28/golang-gc/)

有疑问加站长微信联系

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:812540095

5575 次点击  ∙  1 赞  
加入收藏 微博
被以下专栏收入,发现更多相似内容
1 回复  |  直到 2019-06-02 10:18:05
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传