GO:复用对象和协程资源

避免频繁分配相同类型临时对象的开销

问题 : 不停地创建临时对象,Golang 运行时的哪些操作会消耗 CPU 资源?

  1. 首先是内存分配。我们不停地创建对象时,就得不断地在堆里面找空闲的内存块,然后进行分配。这就像是在一个大仓库里,每次创建新对象都要重新找地方放,这个过程是很消耗资源的.

  2. 垃圾收集(GC)。要是临时对象很多,那在进行垃圾收集的时候,就需要耗费更多的 CPU 资源去扫描这些对象,看看哪些没用了,然后清理掉。这就像大扫除的时候,如果杂物太多,打扫起来就会更费劲。

问题: 如何避免频繁创建临时对象而产生的内存分配和 GC 开销?

由于我们每次申请的都是同一种类型的对象,也就是 bytes.Buffer。既然这样,像下面的图一样,我们可以在每次用完这个临时对象之后,把它放到一个池子里。等下次要用的时候,就直接从池子里取出来用。这样一来,就不用每次都从堆里重新分配,而且 GC 需要扫描的临时对象也会减少,这就是对象池的思想。
GO 如何创建对象池?

对于对象池,Golang 里有个 sync.Pool 类型来支持

Go 复制代码
var objectPool = sync.Pool{
    New: func() interface{} {
        return &bytes.Buffer{}
    },
}

func WriteBufferWithPool() {
    // 获取临时对象
    buf := objectPool.Get().(*bytes.Buffer)
    // 使用
    buf.Write(data)
    // 将buf对象里面的字段恢复初始值
    buf.Reset()
    // 放回
    objectPool.Put(buf)
}

从 CPU 资源消耗来看,不使用对象池,单次函数调用要 232.2ns,而使用对象池的方式,只要 32.81ns,节约了 86% 左右的 CPU 资源。

从内存消耗来看,使用对象池的方式,平均单次请求从堆中分配的大小和次数直接变成了 0。

问题:协程池:如何避免频繁创建协程的开销?

第一就是内存分配。在 Golang 中,每次创建一个协程,默认都得分配 2KB 的内存空间,而且还要进行初始化。要是不停地创建协程,那就得不停地进行内存分配和初始化,这个过程就需要消耗不少 CPU 资源。

第二就是协程调度。特别是那种 CPU 密集型的应用,如果频繁创建协程,将会导致处于就绪状态、能被 CPU 调度的协程过多。协程调度就会变得很频繁,要知道,协程调度这个过程本身也是会消耗 CPU 资源的。

相关推荐
ArixBit1 小时前
用 Go 手撸 Agent 框架 #11:工具从哪里来——内置工具、MCP 与 Skills
go·agent
程序员爱钓鱼4 小时前
为什么学习 Go?Go 能做什么?
后端·面试·go
用户0953675158319 小时前
在系统的学习 redis 前的疑惑
redis·go
名字还没想好☜1 天前
Go 用 errgroup 管理并发子任务:错误收敛、取消传播与限流
开发语言·后端·golang·go·并发
labixiong1 天前
TypeScript 7.0 编译器用 Go 重写,速度暴增10倍——背后到底做了什么?
前端·javascript·go
名字还没想好☜2 天前
Go 的 time.Ticker 陷阱:定时任务里被忽略的内存泄漏与正确关闭
java·数据库·golang·go·定时器
ArixBit2 天前
用 Go 手撸 Agent 框架 #10:用自己的 Runtime,做一个研究助手
go·agent
cocoCola916673 天前
validator库参数校验若干实用技巧
go
不会C语言的男孩3 天前
第三章:流程控制
go
名字还没想好☜3 天前
Go 的 sync.Pool 实战:复用对象把 GC 压力打下来
开发语言·后端·性能优化·golang·go