内存逃逸
内存分配有两种方式 : 堆分配 和 栈分配
内存逃逸 :Go中程序变量会携带一组校验数据,用来证明它的整个生命周期在程序运行时是否完全可知。如果变量通过了这些校验,它就可以在栈上分配,反之就可以说它逃逸了,这时就必须在堆上分配。
这样做虽然浪费堆空间,但是有效避免了悬挂指针的出现,并且由于GC的存在也不会出现内存泄漏,权衡之下也是一种合理的做法。
出现内存逃逸的情况:
指针逃逸
在方法内把局部变量指针返回时,会出现内存逃逸。因为局部变量原本应该在栈上分配,并且在栈中回收,但是由于在返回时被外部引用,因此该变量的生命周期大于栈,这时就会发生内存溢出。
- 栈空间不足逃逸
当栈空间不足时会分配到堆上。
发送指针或是带有指针的值到channel中
因为在代码编译的时候,是没有办法知道是哪个goroutine会在channel上接收数据,所以编译器没办法知道变量什么时候才会被释放。
切片中存储指针或是带有指针的值
因为尽管其后面的数组可能是在栈上分配的,但其引用的值一定是在堆上。
slice数组扩容可能导致内存逃逸
如果在程序运行时扩容,就会在堆上分配
interface类型
因为interface类型可以代表任意类型,编译器不知道参数会是什么类型,只有运行时才知道,因此只能分配到堆上。
指针传递比值传递更高效,但是在Go中并非如此,如果指针传递出现内存逃逸将内存分配到堆上后续就有会GC操作,消耗比值传递更大。
内存溢出
指申请内存时,没有足够内存空间
出现原因:
-
内存值设定过小
-
内存中加载数据量过于庞大
-
太多内存分配后没有回收,出现内存泄漏
内存泄漏
指申请了内存,但没有释放,导致这些内存无法被使用,内存空间浪费的情况。
Go虽然有GC来回收,但是还是会出现内存泄漏的问题。
在Go中发现内存泄露有2种方法,一个是通用的监控工具,另一个是go pprof:
- 监控工具:固定周期对进程的内存占用情况进行采样,数据可视化后,根据内存占用走势(持续上升),很容易发现是否发生内存泄露。
- go pprof:适合没有监控工具的情况,使用Go提供的pprof工具判断是否发生内存泄露。
pprof 可以参考