当前位置:   article > 正文

Go内存溢出与内存泄露_go out of memory

go out of memory

https://www.cnblogs.com/sunsky303/p/11077030.html

一、内存泄露与内存溢出的区别

内存溢出(out of memory,简称OOM) 

内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,简单点说就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出出现out of memory异常。

内存泄露(memory leak) 

内存泄露是指程序在申请内存后,无法释放已申请的内存空间,简单点说就是你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。

内存泄露指的是程序运行过程中已不再使用的内存,没有被释放掉,导致这些内存无法被使用,直到程序结束这些内存才被释放的问题。

Go虽然有GC来回收不再使用的堆内存,减轻了开发人员对内存的管理负担,但这并不意味着Go程序不再有内存泄露问题。

分配的内存不足以放下数据项序列,称为内存溢出

对于 C 和 C++ 这种没有 Garbage Collection 的语言来讲,我们主要关注两种类型的内存泄漏

1、堆内存泄漏(Heap leak)

        堆内存指的是程序运行中根据需要分配通过 malloc,realloc new 等从堆中分配的一块内存,再是完成后必须通过调用对应的 free 或者 delete 删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生 Heap Leak。


2、系统资源泄露(Resource Leak)

        主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET 等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。

不是所有的out of memory都是memory leak导致的,但是memory leak会最终会导致out of memory!

二、内存泄漏定位:

关于Go的内存泄露:10次内存泄露,有9次是goroutine泄露。

所以:掌握了如何定位和解决goroutine泄露,就掌握了Go内存泄露的大部分场景

2.1: pprof工具:

pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等,当需要性能调优或者定位Bug时候,这些记录的信息是相当重要。

基本使用:

使用pprof有多种方式,Go已经现成封装好了1个:net/http/pprof,使用简单的几行命令,就可以开启pprof,记录运行信息,并且提供了Web服务,能够通过浏览器和命令行2种方式获取运行数据。

看个demo:

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. _ "net/http/pprof"
  6. )
  7. func main() {
  8. // 开启pprof,监听请求
  9. ip := "0.0.0.0:6060"
  10. if err := http.ListenAndServe(ip, nil); err != nil {
  11. fmt.Printf("start pprof failed on %s\n", ip)
  12. }

输入网址ip:port/debug/pprof/打开pprof主页,从上到下依次是5类profile信息

  1. block:goroutine的阻塞信息
  2. goroutine:所有goroutine的信息,下面的full goroutine stack dump是输出所有goroutine的调用栈,是goroutine的debug=2,后面会详细介绍。
  3. heap:堆内存的信息
  4. mutex:锁的信息
  5. threadcreate:线程信息

 

使用命令go tool pprof url可以获取指定的profile文件,此命令会发起http请求,然后下载数据到本地之后进入交互式模式,就像gdb一样,可以使用命令查看运行信息,以下是5类请求的方式:

  1. # 下载cpu profile,默认从当前开始收集30s的cpu使用情况,需要等待30s
  2. go tool pprof http://localhost:6060/debug/pprof/profile # 30-second CPU profile
  3. go tool pprof http://localhost:6060/debug/pprof/profile?seconds=120 # wait 120s
  4. # 下载heap profile
  5. go tool pprof http://localhost:6060/debug/pprof/heap # heap profile
  6. # 下载goroutine profile
  7. go tool pprof http://localhost:6060/debug/pprof/goroutine # goroutine profile
  8. # 下载block profile
  9. go tool pprof http://localhost:6060/debug/pprof/block # goroutine blocking profile
  10. # 下载mutex profile
  11. go tool pprof http://localhost:6060/debug/pprof/mutex

请求之后会展示以下信息: 

  1. 下载得到的文件:/home/ubuntu/pprof/pprof.demo.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz,这其中包含了程序名demo,profile类型alloc已分配的内存,inuse代表使用中的内存。
  2. help可以获取帮助,最先会列出支持的命令,想掌握pprof,要多看看,多尝试。关于命令,本文只会用到3个,我认为也是最常用的:toplisttraces,分别介绍一下。

  1. top:显示正运行到某个函数goroutine的数量
  2. traces:显示所有goroutine的调用栈
  3. list:列出代码详细的信息。

top会列出5个统计数据:

  • flat: 本函数占用的内存量。
  • flat%: 本函数内存占使用中内存总量的百分比。
  • sum%: 前面每一行flat百分比的和,比如第2行虽然的100% 是 100% + 0%。
  • cum: 是累计量,加入main函数调用了函数f,函数f占用的内存量,也会记进来。
  • cum%: 是累计量占总量的百分比。

怎么发现内存泄露

  1. 监控工具:固定周期对进程的内存占用情况进行采样,数据可视化后,根据内存占用走势(持续上升),很容易发现是否发生内存泄露。
  2. go pprof:适合没有监控工具的情况,使用Go提供的pprof工具判断是否发生内存泄露

总结

goroutine泄露的本质

goroutine泄露的本质是channel阻塞,无法继续向下执行,导致此goroutine关联的内存都无法释放,进一步造成内存泄露。

goroutine泄露的发现和定位

利用好go pprof获取goroutine profile文件,然后利用3个命令top、traces、list定位内存泄露的原因。

goroutine泄露的场景

泄露的场景不仅限于以下两类,但因channel相关的泄露是最多的。

  1. channel的读或者写:

    1. 无缓冲channel的阻塞通常是写操作因为没有读而阻塞
    2. 有缓冲的channel因为缓冲区满了,写操作阻塞
    3. 期待从channel读数据,结果没有goroutine写
  2. select操作,select里也是channel操作,如果所有case上的操作阻塞,goroutine也无法继续执行。

编码goroutine泄露的建议

为避免goroutine泄露造成内存泄露,启动goroutine前要思考清楚:

  1. goroutine如何退出?
  2. 是否会有阻塞造成无法退出?如果有,那么这个路径是否会创建大量的goroutine?

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/428406
推荐阅读
相关标签
  

闽ICP备14008679号