Golang垃圾收集原理

        Go编译生成的目标程序会嵌入一个go运行时,里面包括一个垃圾收集器,类似java的虚拟机中的垃圾收集器,它负责收集并清理不再使用的内存空间。

不在垃圾收集范围内

        Go的标量基本数据类型不在垃圾收集的范围内,因为它们存在于goroutine stack 中,随着方法的结束而释放,这些变量类型有 uint、byte、指针等,垃圾收集主要管理堆区的内存。

垃圾收集采用的方式

        Go垃圾收集采用 标记-清理 方式来管理内存,当触发垃圾收集阈值时,首先标记不再适用的内存,然后清理掉用它们。

标记内存的方式

        采用对象图方式,标记初始阶段,go 会枚举根节点roots,能够作为根节点的有 本地变量、全局变量,从roots开始分析所有不可到达的内存引用。

GC的成本

        1.GC运行时程序代码会暂停

        2.GC会消耗CPU和内存

GO垃圾收集(简称GOGC)方式

        GOGC根据当前活动堆大小使用配置的GOGC值(允许的当前Live Heap峰值内存百分比)来决定是否启动垃圾收集,比如当前活动堆大小20MB,GOGC 设置为 100 ,当新分配内存堆峰值内存大于20MB时将触发垃圾收集。

如图

可以知道当GOGC设置的越小垃圾收集就会越频繁。

Go MemoryLimit(在Go 1.19版本可用)

        设置Go运行时可使用的堆内存,该限制并不是强制性,如设置Limit=20MB,程序实际需要100MB,实际也能够分配到100MB,但是垃圾收集器会频繁进行GC来试图保持不可能到达的Limit值。

GOGC配置推荐规则

 1.当部署程序到完全可控的环境下,建议根据实际需要设置MemoryLimit。

 2.GO程序与其它程序一起运行时不要关闭GOGC,关闭GOGC会导致耗尽内存影响到其它进程。

 3.部署到不可控的环境下时不要设置MemoryLimit(如 云环境 容器等)

GOGC实践(由于GO 1.19还未发布,无法测试MemoryLimit特性)

func main() {
	//设置GOGC峰值分配内存百分比
	debug.SetGCPercent(100)
	data := make([][]byte, 0)
	for index := 0; index < 10; index++ {
		//分配100MB
		var buff []byte = make([]byte, 1024*1024*100)
		for i := range buff {
			buff[i] = 111
		}
		//data = append(data, buff)
		time.Sleep(time.Duration(2 * time.Second))
		fmt.Println(len(buff))
		fmt.Println(len(data))
	}
}

 设置GOGC=100,在整个运行期间将保持合适的内存,程序总共分配了1G内存,活动堆内存为100MB,由于GOGC设置了100%,所以分配的峰值内存为100MB,所以程序始终保持200MB左右的内存。

 

保持分配内存存活(关联gc roots节点),GOGC=100,每次分配200MB,其中100MB关联到roots链,另外100MB不关联,当程序运行时始终保持合适的内存值。

 

关闭GOGC时,内存占用会保持实际分配的内存量