内存逃逸

内存分配有两种方式 : 堆分配栈分配

内存逃逸 :Go中程序变量会携带一组校验数据,用来证明它的整个生命周期在程序运行时是否完全可知。如果变量通过了这些校验,它就可以在栈上分配,反之就可以说它逃逸了,这时就必须在堆上分配。

这样做虽然浪费堆空间,但是有效避免了悬挂指针的出现,并且由于GC的存在也不会出现内存泄漏,权衡之下也是一种合理的做法。

出现内存逃逸的情况:

  • 指针逃逸

    在方法内把局部变量指针返回时,会出现内存逃逸。因为局部变量原本应该在栈上分配,并且在栈中回收,但是由于在返回时被外部引用,因此该变量的生命周期大于栈,这时就会发生内存溢出。

  • 栈空间不足逃逸
    当栈空间不足时会分配到堆上。
  • 发送指针或是带有指针的值到channel中

    因为在代码编译的时候,是没有办法知道是哪个goroutine会在channel上接收数据,所以编译器没办法知道变量什么时候才会被释放。

  • 切片中存储指针或是带有指针的值

    因为尽管其后面的数组可能是在栈上分配的,但其引用的值一定是在堆上。

  • slice数组扩容可能导致内存逃逸

    如果在程序运行时扩容,就会在堆上分配

  • interface类型

    因为interface类型可以代表任意类型,编译器不知道参数会是什么类型,只有运行时才知道,因此只能分配到堆上。

指针传递比值传递更高效,但是在Go中并非如此,如果指针传递出现内存逃逸将内存分配到堆上后续就有会GC操作,消耗比值传递更大。


内存溢出

指申请内存时,没有足够内存空间

出现原因:

  • 内存值设定过小

  • 内存中加载数据量过于庞大

  • 太多内存分配后没有回收,出现内存泄漏


内存泄漏

指申请了内存,但没有释放,导致这些内存无法被使用,内存空间浪费的情况。

Go虽然有GC来回收,但是还是会出现内存泄漏的问题。

在Go中发现内存泄露有2种方法,一个是通用的监控工具,另一个是go pprof:

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

pprof 可以参考