内存泄露,是从操做系统的角度上来阐述的,形象的比喻就是“操做系统可提供给全部进程的存储空间(虚拟内存空间)正在被某个进程榨干”,致使的缘由就是程序在运行的时候,会不断地动态开辟的存储空间,这些存储空间在在运行结束以后后并无被及时释放掉。应用程序在分配了某段内存以后,因为设计的错误,会致使程序失去了对该段内存的控制,形成了内存空间的浪费。golang
若是程序在内存空间内申请了一块内存,以后程序运行结束以后,没有把这块内存空间释放掉,并且对应的程序又没有很好的gc机制去对程序申请的空间进行回收,这样就会致使内存泄露。性能
首先标记root根对象,根对象的子对象也是存活的。优化
根对象包括:全局变量,各个G stack上的变量等。spa
span是内存管理的最小单位,因此猜想gc的粒度也是span。操作系统
如图所示,经过gcmarkBits位图标记span的块是否被引用。对应内存分配中的bitmap区。设计
例如,当前内存中有A~F一共6个对象,根对象a,b自己为栈上分配的局部变量,根对象a、b分别引用了对象A、B, 而B对象又引用了对象D,则GC开始前各对象的状态以下图所示:指针
stop the world是gc的最大性能问题,对于gc而言,须要中止全部的内存变化,即中止全部的goroutine,等待gc结束以后才恢复。orm
GO的GC是并行GC, 也就是GC的大部分处理和普通的go代码是同时运行的, 这让GO的GC流程比较复杂.对象
目前整个GC流程会进行两次STW(Stop The World), 第一次是Stack scan阶段, 第二次是Mark Termination阶段.blog
从1.8之后的golang将第一步的stop the world 也取消了,这又是一次优化; 1.9开始, 写屏障的实现使用了Hybrid Write Barrier, 大幅减小了第二次STW的时间.
由于go支持并行GC, GC的扫描和go代码能够同时运行, 这样带来的问题是GC扫描的过程当中go代码有可能改变了对象的依赖树。
例如开始扫描时发现根对象A和B, B拥有C的指针。
为了不这个问题, go在GC的标记阶段会启用写屏障(Write Barrier).
启用了写屏障(Write Barrier)后,在GC第三轮rescan阶段,根据写屏障标记将C放入灰色,防止C丢失。