什么是三色标记法
三色标记法是一种用于垃圾回收的算法,被广泛应用于各类编程语言的垃圾回收器中,包括Go语言。
这种算法将对象划分成三种颜色:
白色:表示对象是垃圾,可以被释放。也就是说,这是那些从根(root)节点开始无法触及的对象。
黑色:表示对象是存活的,并且这个对象引用的所有对象也已被访问和标记。
灰色:表示对象是存活的,但是这个对象引用的其他对象还没有被访问和标记。
算法开始时,所有对象都标记为白色,然后从根节点开始遍历,遍历到的对象标记为灰色。接着选择一个灰色对象,把它引用的所有白色对象都标记为灰色,然后把自己标记为黑色。这个过程一直进行,直到没有灰色对象。此时,所有存活的对象都被标记为黑色,而所有死亡(不可达或无用)的对象都仍然是白色,GC会回收这些白色对象。
整个过程中的重点在于,如果所有的灰色对象指向的对象都已经处理(标记为黑色),则说明所有的可达对象都已访问(标记为黑色),非黑即白,那么所有的白色对象为不可达对象,可被清理。
Go垃圾回收机制
Go语言的垃圾回收(Garbage Collection,简称GC)机制是自动回收系统中不再使用的内存的功能。这对于开发者来说非常方便,因为它省去了手动管理内存的需求。
垃圾回收机制在Go语言中的执行过程主要由以下几个步骤组成:
标记清除:首先,垃圾回收器会标记出所有生存对象,然后清除所有没有被标记的对象。
并发标记:在这个阶段,垃圾回收器会在用户goroutine后台进行,并试图在标记阶段结束之前标记所有的生存对象。
标记终止:在这个阶段,所有的用户goroutine都会停止,垃圾回收器会进行最后的检查并清除所有未标记的对象。
扫描:在清除了所有未标记的对象之后,垃圾回收器将进行内存清扫,归还未使用的内存给操作系统。
其中,“并发标记清除”是Go的垃圾回收器的一大特点,它大部分时间与用户的goroutine并发执行,只有在GC的开始和结束阶段才会暂停goroutine。
Go的GC还支持开发者通过runtime的方法来进行设置和调整,比如设置GC百分比、手动触发GC等,以满足更精细化的内存管理需求。
Stop The World
Go语言在执行垃圾回收时,会有两次STW(Stop The World,简称停工)事件。
第一次停工事件发生在垃圾回收的"并发标记"阶段开始之前,这次停工是为了确保所有的goroutine都写回自己的栈信息,使垃圾回收器能够正确地找到所有根对象。这次停工的时间通常非常短,因为对栈进行扫描的操作一般很快就能完成。
第二次停工事件发生在"并发标记"阶段结束后,垃圾回收进入到"标记终止"阶段。这次停工主要是为了处理在"并发标记"阶段可能遗漏掉的对象。这个阶段包括处理分配在"并发标记"阶段的对象,处理在"并发标记"阶段产生的写屏障,以及对heap进行最后一次扫描。这次停工的时间与heap的大小、写屏障的数量以及"并发标记"阶段分配的对象数量有关,通常也会尽力控制在可以接受的范围之内。
总的来说,Go语言尽量减少了垃圾回收中的停工时间,同时通过并发进行垃圾回收,使得Go语言的垃圾回收相较于其他一些语言的垃圾回收,对程序执行的影响更小。
以下情况可能会影响Go语言的STW (Stop The World) 的时间:
大量的内存使用:如果程序使用了大量内存,垃圾回收器需要扫描更多的对象,这就可能增加STW的时长。
大量的 Goroutine:每个 Goroutine 都会增加STW的时间,因为在两次STW期间,它们的调度状态和栈必须被扫描。
写屏障的开启状态:在并发标记阶段,一旦对象被灰度标记,如果应用程序试图更改该对象,必须先开启写屏障来标记任何新分配或者即将被覆盖的对象。因此,过多的写操作会增加STW的时间。
分配速度:在垃圾回收过程中,如果内存分配的速度快于垃圾回收的速度,可能会导致内存空间不断增长,从而延长STW的时间。
这些情形都可能导致STW时间的增加,因此在编写代码时,开发者需要尽量减少内存的使用,控制 Goroutine 的数量,注意内存分配的速度等,以更好地管理内存并提高程序的性能。