GC是什么?
GC(Garbage Collection)即垃圾回收,实质上就是回收垃圾内存。由于栈上的内存是有编译器分配和释放的,所以GC都是对堆上不再使用的内存进行回收,让这些内存能够再次被利用
为什么需要GC?
在真实业务场景中,对象个数非常多,引用关系错综复杂,如果全靠程序员手动释放内存很难避免指针悬挂错误。所以一门拥有自动识别并回收垃圾内存的语言(如Java、Python、Go)在开发效率上是远远高于没有GC机制的语言的
常见的GC算法
引用计数法
引用计数法会为每一个对象分配一个空间(“计数器”),这个空间专门用来存储对象被引用的次数。如果有其他其他对象引用A对象,那么A对象的计数器就会+1;反之,引用A的对象删除对A的引用后,A的计数器便会-1,当计数器减至0时,该对象的内存空间就会被回收
- 优点:简单直接,回收速度快,不会出现内存耗尽或达到阈值才进行回收的现象
- 缺点:
- 需要额外空间维护对象的“引用”
- 无法解决循环引用问题
标记清除法
标记清除法可以分为两步:
标记对象:从根对象开始,遍历所有的对象及其子对象,并标记它们的可达状态
清除对象:再一次遍历堆中中所有的对象,将没有被标记的对象删除
优点:简单易实现,不需要额外的存储空间,适用于对象个数少的场景
缺点:会造成内存碎片,导致后续创建大对象时,即使有足够的内存空间也可能无法成功创建,降低了内存的使用率
复制法
复制法将内存分为大小相等的两块区域,每一时刻只能使用其中的一块内存。当一块区域的内存被用完时,就需要把该区域还在使用的对象移动到另一块区域,同时将已使用的内存全部清除
- 优点:不会产生内存碎片,每次都是清除整块内存
- 缺点:内存被一分为二,利用率低,如果对象个数较多,复制对象的过程耗时长,效率低下
标记整理法
标记整理法和标记清除法类似,同样是需要两阶段完成GC操作
- 标记阶段:从GC的根节点遍历所有的对象,把还在使用的对象标记
- 整理阶段:所有存活的对象被移动到空闲内存的一端,按照地址顺序排列,并更新对象引用指针。最后将末端地址之外的内存空间清除
Go语言的GC算法
Go语言的GC采用的是并发三色标记法 + 屏障技术实现的。三色标记法本质就是标记清理算法,并没有对象分代、内存整理的行为,三色标记只是对其标记阶段的一种简单描述。
为什么不需要内存整理(为什么不采用标记整理算法或复制算法 )?
Go语言的内存分配算法是基于TCMalloc,虽然不能像标记整理法/复制法消除内存碎片,但也能有效的降低内存碎片率。此外,Thread Cache机制使得Go在大部分分配场景下可以避免使用锁,在高并发环境下具有极佳的性能优势
为什么不采用分代法?
分代法的最大优势就是区分长生命周期和短生命周期的对象,从而快速回收段生命周期对象。但Go语言在编译期会做逃逸分析,可以将短生命周期的struct分配到栈上,考虑到一方面栈上的内存回收是非常快的,另一方面在Go中短生命周期对象个数并不多,所以分代法在Go中优势并不明显。相反地,分代法需要额外的屏障来维护老年代对象对新年代对象的引用关系,增加了GC负担
Go的GC
标记清除法的瓶颈
标记清除法在GC之前需要STW,即暂停这个应用程序,否则会导致错误地删除被引用的对象。但STW会给引用性能带来极大的损耗
是什么,有什么优势,实际情况,特殊性,不适用