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

相关推荐
Wo3Shi4七17 分钟前
数组
数据结构·算法·go
DemonAvenger1 小时前
Go内存压力测试:模拟与应对高负载的技术文章
性能优化·架构·go
DemonAvenger1 小时前
从C/C++迁移到Go:内存管理思维转变
性能优化·架构·go
程序员爱钓鱼1 小时前
Go语言项目工程化 —— 日志、配置、错误处理规范
后端·google·go
没逻辑1 小时前
Go 服务架构性能优化指南(实战精选)
后端·性能优化·go
DemonAvenger1 小时前
Go网络编程基础:网络模型与协议栈概述
网络协议·架构·go
「、皓子~3 小时前
后台管理系统的诞生 - 利用AI 1天完成整个后台管理系统的微服务后端+前端
前端·人工智能·微服务·小程序·go·ai编程·ai写作
inhere4 小时前
gookit/goutil v0.7.0 新版本发布:模块调整与功能增强
开源·go·github
pltrue5 小时前
Go 重构案例分享:订单创建逻辑重构
go·php
梦兮林夕5 小时前
02 gRPC 语法及类型介绍
后端·go·grpc