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 资源的。

相关推荐
一个热爱生活的普通人8 小时前
Go语言中 Mutex 的实现原理
后端·go
孔令飞8 小时前
关于 LLMOPS 的一些粗浅思考
人工智能·云原生·go
小戴同学8 小时前
实时系统降低延时的利器
后端·性能优化·go
woodWu1 天前
白话kotlin协程
协程
Golang菜鸟1 天前
golang中的组合多态
后端·go
Serverless社区1 天前
函数计算支持热门 MCP Server 一键部署
go
Wo3Shi4七1 天前
二叉树数组表示
数据结构·后端·go
网络研究院1 天前
您需要了解的有关 Go、Rust 和 Zig 的信息
开发语言·rust·go·功能·发展·zig
27669582921 天前
拼多多 anti-token unidbg 分析
java·python·go·拼多多·pdd·pxx·anti-token
程序员爱钓鱼2 天前
Go 语言邮件发送完全指南:轻松实现邮件通知功能
后端·go·排序算法