如何优化golang gc

本文介绍了Go语言的GOGC机制,包括GC耗时、堆大小对GC的影响以及三色标记算法。同时,提供了查看GC信息的方法,如GODEBUG、gotooltrace和debug.ReadGCStats。此外,讨论了并发GC、非对齐内存分配等优化技巧,以及如何调整GOGC参数来改善性能。
摘要由CSDN通过智能技术生成

目录

一.理解GO GC机制

1.1GC的耗时

1.2堆大小对GC的影响

1.3GC算法

二 如何查看GC信息

2.1使用GODEBUG="gotrace=1"

2.2 go tool trace

2.3 debug.ReadGCStats

​编辑2.4 runtime.ReadMemStats

三 GC优化技巧 

3.1并发GC

3.2非对齐内存分配

3.3手动触发GC

3.4调整GOGC

3.4避免使用GC

推荐:

Golang GC 三色标记法_wangxiaoangg的博客-CSDN博客

一.理解GO GC机制

GO语言采用的是三色标记法垃圾回收算法,这种算法采用的是并发标记,扫描完成后再进行清除工作,这意味着性能上可能会付出一些代价。

1.1GC的耗时

在垃圾收集周期内,所有的goroutine必须停止运行以便垃圾回收器可以尽可能多地清除内存。GC会带来一些显著的延迟,大部分gc耗时都集中于标记阶段。越大的堆空间,GC的时间也越长。

1.2堆大小对GC的影响

每个程序都有一个堆大小,这个大小是通过runtime.GOMAXPROCS和runtime.GOGC这两个环境变量来控制的。runtime.GOGC默认是100,意味着当达到100%的可用内存时,GC将开始在程序运行期间自动执行垃圾回收。而runtime.GOMAXPROCS控制程序并发时最大的goroutine数量。

//todo举个例子

1.3GC算法

Go语言采用三色标记算法的主要优点是能够在很短的时间内清理整个堆,避免了没有必要的清理操作。三色标记算法具有最小停顿时间和实时清理堆的能力,但是,它需要非常高的内存扫描性能。这是由于算法需要对对象执行读取和写入操作。所以我们需要在内存使用量和最小停顿时间之间进行平衡。

//todo 三色标记法

二 如何查看GC信息

2.1使用GODEBUG="gotrace=1"

package main

func main() {
	//循环分配很大内存,触发GC
	for n := 1; n < 100; n++ {
		a := make([]byte, 1<<20)
		a = a
	}
}

 命令行使用 GODEBUG="gctrace=1"

字段解析: 

gc 2这是第2次gc。
@0.041s 这次gc的markTermination阶段完成后,距离runtime启动到现在的时间。
 1%

到目前为止,gc的标记工作(包括两次mark阶段的STW和并发标记)所用的CPU时间占总CPU的百分比

 0.053+0.95+0.004 ms clock,按顺序分成三部分
0.053表示mark阶段的STW时间(单P的);
0.95表示并发标记用的时间(所有P的);
0.004表示markTermination阶段的STW时间(单P的)。
0.21+0.093/0.35/0.60+0.018 ms cpu按顺序分成三部分
0.21表示整个进程在mark阶段STW停顿时间(0.013 * 8);

0.093/0.35/0.60有三块信息,0.093是mutator assists占用的时间,
0.35是dedicated mark workers+fractional mark worker占用的时间,
0.60是idle mark workers占用的时间。

0.018 ms表示整个进程在markTermination阶段STW停顿时间。
 3->3->0 MB,

按顺序分成三部分,
3表示开始mark阶段前的heap_live大小;

3表示开始markTermination阶段前的heap_live大小;
0表示被标记对象的大小。

4 MB goal, 0 MB stacks, 0 MB globals,

表示下一次触发GC的内存占用阀值是4MB,

4 P本次gc共有多少个P。

2.2 go tool trace

package main

import (
	"os"
	"runtime/trace"
)

func GoTrace() {
	f, _ := os.Create("trace.out")
	defer f.Close()
	trace.Start(f)
	defer trace.Stop()
	//循环分配很大内存,触发GC
	for n := 1; n < 100; n++ {
		a := make([]byte, 1<<20)
		a = a
	}

}

代码执行完成后可以看到会生成trace.out文案,使用 go tool trace命令查看trace文件

go tool trace -http 127.0.0.1:8080 trace.out

2.3 debug.ReadGCStats

package main

import "time"

func main() {
	go RunTimeDebugReadGc()
	//循环分配很大内存,触发GC
	for n := 1; n < 100; n++ {
		a := make([]byte, 1<<20)
		a = a
	}
	//方便观察 sleep 10s
	time.Sleep(10 * time.Second)
}

func RunTimeDebugReadGc() {
	//每秒触发一次
	tracker := time.NewTicker(1 * time.Second)
	gcStat := debug.GCStats{}
	for {
		select {
		case <-tracker.C:
			debug.ReadGCStats(&gcStat)
			fmt.Println(gcStat)
		}
	}
}



2.4 runtime.ReadMemStats

func RunTimeDebugReadMem() {
	//每秒触发一次gc
	tracker := time.NewTicker(1 * time.Second)
	gcStat := runtime.MemStats{}
	for {
		select {
		case <-tracker.C:
			runtime.ReadMemStats(&gcStat)
			fmt.Println(gcStat)
		}
	}
}

三 GC优化技巧 

3.1并发GC

并发GC是指GC开始之前,应用程序仍然可以继续执行。 Go语言的GC只有在程序不能继续执行时才运行。同样,因为垃圾回收时程序不工作,因此并发垃圾回收可以大大减少程序停止运行的时间。

3.2非对齐内存分配

内存对接分配可以参考

非对齐内存分配是指在堆上申请非对齐内存块。这是一种减少内存碎片的方法。内存碎片是指多个内存块之间的间隙。通常,当使用堆管理器来管理内存时,垃圾回收时需要在堆中移动和调整不同的内存块,这会导致大量的内存拷贝。如果能够减少碎片,GC的效率就会更高。

3.3手动触发GC

手动触发GC是指在程序运行期间显式调用runtime.GC函数。虽然这不是最激进的解决方案,但它可以有效地减少GC的延迟。

3.4调整GOGC

在程序高峰期跑GC可能会对性能造成严重影响。可以适当调整runtime.GOGC的值来减少GC的频率,根据应用程序的工作负载和内存使用模式,应适当调整该变量。

3.4避免使用GC

通过避免在程序中使用不必要的动态内存分配和释放,可以减少GC的频率和延迟。此外,还可以使用对象池来重新使用不再使用的对象,从而不会立即释放内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值