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

相关推荐
审判长烧鸡14 小时前
【Go工具】go-playground是什么组织?官方的?
开发语言·安全·go
别样的感动14 小时前
我写了一个 Go 框架:用 DSL 替代 ORM,代码体积减半,开发效率翻倍
go
明月_清风19 小时前
Go语言空接口与类型断言完全指南:从"万能容器"到"类型还原"
后端·go
蓝宝石的傻话21 小时前
security-collector-exporter:用Prometheus 解决 Linux 的安全审计
go
tyung1 天前
Go 手写二叉堆优先队列:避开 container/heap 的性能陷阱
数据结构·后端·go
审判长烧鸡2 天前
【PHPer转Go】fmt vs log/slog
go·php
漓漾li2 天前
每日面试题(2026-05-20)- GO AI agent全栈
后端·架构·go
.魚肉2 天前
Raft 共识算法 · 演示系统(多终端)
算法·go·raft·分布式系统
审判长烧鸡2 天前
【Go工具】go-playground除了validator还有哪些常用的库
go·web
审判长烧鸡2 天前
Go 新版核心知识点合集(适配 Go1.18+ 含泛型 + 断言 + 接口 + 指针接收者全套)
go