【go】什么是Go语言中的GC,作用是什么?调优,sync.Pool优化,逃逸分析演示

Go 语言中的 GC 简介与调优建议

Go语言GC工作原理

对于 Go 而言,Go 的 GC 目前使用的是无分代(对象没有代际之分)、不整理(回收过程中不对对象进行移动与整理)、并发(与用户代码并发执行)的三色标记清扫算法。
  • 分代:指的是将对象按照存活时间分为新生代(存活时间小)老年代(存活时间久),以及永远不参与回收的永久代
    由此将主要回收目标放到新生代上(存活时间短,更倾向于被回收)。但是go的编译器会通过逃逸分析,将大部分新生代放到栈上(栈会直接回收),而需要长期存储的放在需要进行垃圾回收的堆中
    即需要回收的新生代会随着goroutine栈的销毁而回收,不需要gc的参与,所以go采用不分代。

  • 整理:指的是在对象移动时将他们排列整齐,,意在解决内存碎片问题,“允许”顺序内存分配。但是go运行时分配的算法是tcmalloc,基本上没有碎片问题,并且顺序内存存储在多线程场景下不适用,所以go采用不整理。

二、工作流程(三色标记法)
阶段工作内容是否STW
标记准备暂停应用,启用写屏障,初始化扫描根对象(栈/全局变量等)
并发标记后台扫描对象图,通过写屏障跟踪并发修改
标记终止完成剩余标记,关闭写屏障
并发清除回收白色对象内存
  • 白色对象(可能死亡)被回收器访问到的对象。在回收开始阶段,所有对象均为白色,当回收结束后,白色对象均不可达。
  • 灰色对象(波面)被回收器访问到的对象,但回收器需要对其中的一个或多个指针进行扫描,因为他们可能还指向白色对象。
  • 黑色对象(确定存活)被回收器访问到的对象,其中所有字段都已被扫描,黑色对象中任何一个指针都不可能直接指向白色对象。
  • STW 是 “Stop The World” 的缩写,有时也可以理解为 “Start The World”,但我们通常说的 STW,指的是 “从程序暂停(Stop)到恢复(Start)之间的这段时间”。

垃圾回收开始,全为白对象,标记过程开始,白对象逐渐开始白->灰,当灰色对象所有子节点都扫描完后,灰->黑。整个堆遍历完之后,只剩下黑和白,清理白。

为什么会发生STW?因为垃圾回收时要清理不用的内存,如果对象还在修改,回收时可能出错,所以要,暂停用户代码,专心回收。

三、Go语言中的根对象组成

根对象类型具体内容生命周期扫描频率
全局变量包级变量(var globalVar *T)、常量等程序整个生命周期每次GC标记阶段
Goroutine栈每个goroutine栈帧中的局部变量、函数参数、返回值等Goroutine存活期间每次GC标记阶段
寄存器CPU寄存器中存储的临时指针(如正在参与计算的引用)执行指令期间扫描栈时同步捕获
运行时数据结构runtime.sched管理的全局队列、finalizer队列、sync.Pool缓存对象等运行时管理每次GC标记阶段

四、常见内存回收算法对比
算法实现方式优点缺点Go中的应用场景
标记-清除标记存活对象后直接回收内存利用率高产生内存碎片大对象堆内存回收
复制算法存活对象复制到新空间无碎片、访问局部性好浪费50%空间小对象MCache分配
标记-整理移动存活对象到连续空间无碎片、空间紧凑移动成本高未直接使用

二、GC 调优建议

  1. 减少内存分配次数

    • 尽量重用对象,避免频繁创建和销毁;
    • 处理字符串拼接时推荐使用 strings.Builder 替代 +,可减少中间对象生成;
    • 减少 slice/map 的扩容行为,适当预估容量。

    示例对比:

    // 不推荐:频繁分配新字符串
    s := ""
    for _, str := range list {s += str
    }// 推荐:使用 strings.Builder
    var builder strings.Builder
    for _, str := range list {builder.WriteString(str)
    }
    
  2. 合并小对象,使用对象池

    • 多个小对象可以设计为一个结构体批量分配,减少单独分配;
    • 对于高频使用的临时对象,推荐使用 sync.Pool 复用,避免反复分配和回收。
    var bufPool = sync.Pool{New: func() any {return make([]byte, 1024)},
    }func handler() {buf := bufPool.Get().([]byte)defer bufPool.Put(buf)// 使用 buf ...
    }
    
  3. 调整 GC 触发频率

    Go GC 的触发频率由一个称为 GOGC(GC Percent) 的参数控制,表示堆增长百分比。

    • 默认值是 100,表示堆增长 100% 后触发一次 GC;
    • 增大该值可以减少 GC 次数,提升性能,但会占用更多内存
    • 可以通过代码动态设置触发比例:
    import "runtime/debug"func init() {debug.SetGCPercent(200) // 增加 GC 触发阈值,适用于内存充足场景
    }
    

三、小结

优化方向方法举例
减少分配重用对象、使用 strings.Builder、减少 slice/map 扩容
合并对象多字段合并为结构体、避免小对象碎片化
对象复用使用 sync.Pool 作为临时对象池
调整频率通过 debug.SetGCPercent() 或环境变量 GOGC 设置触发频率
分析工具使用 GODEBUG=gctrace=1pprof 分析 GC 活动和内存使用情况

非常好!下面我带你一步步完成一个使用 sync.Pool 的优化示例,并教你如何使用 Go 的 逃逸分析工具 来判断优化效果。


示例背景:构建 JSON 字符串,频繁分配 []byte

写一个模拟处理请求的函数,返回 JSON 格式的响应字符串。每次处理都分配一个 []byte 缓冲区。


原始版本(每次都分配新的 []byte):

package mainimport ("fmt"
)func handleRequest() {buf := make([]byte, 0, 1024)buf = append(buf, `{"code":200,"message":"ok"}`...)fmt.Println(string(buf))
}func main() {for i := 0; i < 1000; i++ {handleRequest()}
}

每次 make([]byte, 0, 1024) 都会分配新内存,GC 负担重。


优化版本:使用 sync.Pool 复用 []byte

package mainimport ("fmt""sync"
)var bufPool = sync.Pool{New: func() any {// 初始化容量为 1024 的 byte slicereturn make([]byte, 0, 1024)},
}func handleRequest() {buf := bufPool.Get().([]byte)// 重置长度为 0,保留容量buf = buf[:0]buf = append(buf, `{"code":200,"message":"ok"}`...)fmt.Println(string(buf))bufPool.Put(buf)
}func main() {for i := 0; i < 1000; i++ {handleRequest()}
}

通过 sync.Pool,我们复用了 []byte,避免了频繁内存分配,GC 压力大幅减轻。


如何做逃逸分析

Go 编译器可以告诉你变量是否逃逸到堆上。命令如下:

go build -gcflags="-m" main.go

你会看到类似输出(原始版本中):

./main.go:8:6: moved to heap: buf

表示 buf 逃逸到了堆 → 会被 GC 回收

而优化后版本中(使用 sync.Pool)你应该看到:

./main.go:15:6: buf does not escape

说明变量被控制在了栈上,不会被 GC 管理,性能更好。


总结

技术点说明
sync.Pool用于复用临时对象,减少 GC 压力
逃逸分析工具go build -gcflags="-m" 可查看变量是否逃逸到堆
优化场景高频创建/销毁的临时对象,如 []bytestrings.Builder
注意事项使用 sync.Pool 后的对象必须手动重置状态,避免脏数据

https://github.com/0voice

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【unity实战】Animator启用root motion根运动动画,实现完美的动画动作匹配

文章目录 前言1、动画分类2、如何使用根位移动画&#xff1f; 一、根位移动画的具体使用1、导入人形模型2、导入动画3、配置动画参数4、配置角色Animator动画状态机5、使用代码控制人物前进后退 二、问题分析三、Humanoid动画中的Root Motion机制及相关配置1、Humanoid动画中的…

中间件--ClickHouse-10--海量数据存储如何抉择ClickHouse和ES?

在Mysql数据存储或性能瓶颈时&#xff0c;采用冷热数据分离的方式通常是一种选择。ClickHouse和Elasticsearch&#xff08;ES&#xff09;是两个常用的组件&#xff0c;但具体使用哪种组件取决于冷数据的存储目的、查询模式和业务需求等方面。 1、核心对比 &#xff08;1&…

服务器运维:服务器流量的二八法则是什么意思?

文章目录 用户行为角度时间分布角度应用场景角度 服务器流量的二八法则&#xff0c;又称 80/20 法则&#xff0c;源自意大利经济学家帕累托提出的帕累托法则&#xff0c;该法则指出在很多情况下&#xff0c;80% 的结果是由 20% 的因素所决定的。在服务器流量领域&#xff0c;二…

springboot对接豆包大模型

文档地址: 豆包大模型-火山引擎 模型广场地址: 账号登录-火山引擎 首先来到模型广场&#xff0c;选取你需要的模型,我这边要做图片理解的应用&#xff0c;所以选用了Doubao-1.5.vision-pro. 点立即体验&#xff0c;进入一个新的页面&#xff0c;可以上传图片&#xff0c;然后…

数据通信学习笔记之OSPF其他内容3

对发送的 LSA 进行过滤 当两台路由器之间存在多条链路时&#xff0c;可以在某些链路上通过对发送的 LSA 进行过滤&#xff0c;减少不必要的重传&#xff0c;节省带宽资源。 通过对 OSPF 接口出方向的 LSA 进行过滤可以不向邻居发送无用的 LSA&#xff0c;从而减少邻居 LSDB 的…

智能安全用电系统预防电气线路老化、线路或设备绝缘故障

智能安全用电系统预防电气线路老化、线路或设备绝缘故障 智能安全用电系统&#xff0c;犹如一位忠实而敏锐的卫士&#xff0c;主要针对低压供电网中一系列潜在的危险状况进行了全方位且行之有效的预防和保护。 智能安全用电系统在低压供电网这个复杂的体系中&#xff0c;电气线…

使用Intel Advisor工具分析程序

使用Intel Advisor工具分析程序 Intel Advisor是一款性能分析工具&#xff0c;主要用于识别代码中的向量化机会、线程化和内存访问模式等问题。以下是使用Intel Advisor分析程序的基本步骤&#xff1a; 安装与准备 从Intel官网下载并安装Intel Advisor&#xff08;通常作为I…

【UniApp】Vue2 scss 预编译器默认已由 node-sass 更换为 dart-sass

从 HBuilderX 4.56 &#xff0c;vue2 项目也将默认使用 dart-sass 预编译器。 vue2开发者sass预处理注意&#xff1a; sass的预处理器&#xff0c;早年使用node-sass&#xff0c;也就是vue2最初默认的编译器。 sass官方推出了dart-sass来替代。node-sass已经停维很久了。 另…

智慧能源安全新纪元:当能源监测遇上视频联网的无限可能

引言&#xff1a;在数字化浪潮席卷全球的今天&#xff0c;能源安全已成为国家安全战略的重要组成部分。如何构建更加智能、高效的能源安全保障体系&#xff1f;能源安全监测平台与视频监控联网平台的深度融合&#xff0c;正为我们开启一扇通向未来能源管理新世界的大门。这种创…

C++游戏服务器开发之⑦redis的使用

目录 1.当前进度 2.守护进程 3.进程监控 4.玩家姓名添加文件 5.文件删除玩家姓名 6.redis安装 7.redis存取命令 8.redis链表存取 9.redis程序结构 10.hiredisAPI使用 11.基于redis查找玩家姓名 12.MAKEFILE编写 13.游戏业务实现总结 1.当前进度 2.守护进程 3.进程监…

db中查询关于null的sql该怎么写

正确示例 # 等于null select * from 表名 where 字段名 is NULL; # 不等于null select * from 表名 where 字段名 is not NULL;若需要同时判断字段不等于某个值且不为null select * from users where age ! 30 and age is not null; select * from users where age ! 30 or a…

从“堆料竞赛”到“体验深耕”,X200 Ultra和X200s打响手机价值升维战

出品 | 何玺 排版 | 叶媛 vivo双旗舰来袭&#xff01; 4月21日&#xff0c;vivo X系列春季新品发布会盛大开启&#xff0c;带来了一场科技与创新的盛宴。会上&#xff0c;消费者期待已久的X200 Ultra及X200s两款旗舰新品正式发布。 vivo两款旗舰新品发布后&#xff0c;其打破…

多模态大语言模型arxiv论文略读(三十二)

Proximity QA: Unleashing the Power of Multi-Modal Large Language Models for Spatial Proximity Analysis ➡️ 论文标题&#xff1a;Proximity QA: Unleashing the Power of Multi-Modal Large Language Models for Spatial Proximity Analysis ➡️ 论文作者&#xff1a…

基于贝叶斯优化的Transformer多输入单输出回归预测模型Bayes-Transformer【MATLAB】

Bayes-Transformer 在机器学习和深度学习领域&#xff0c;Transformer模型已经广泛应用于自然语言处理、图像识别、时间序列预测等多个领域。然而&#xff0c;在一些实际应用中&#xff0c;我们面临着如何高效地优化模型超参数的问题。贝叶斯优化&#xff08;Bayesian Optimiz…

Ruby 正则表达式

Ruby 正则表达式 引言 正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是一种强大的文本处理工具&#xff0c;在编程和数据处理中有着广泛的应用。Ruby 作为一种动态、灵活的编程语言&#xff0c;同样内置了强大的正则表达式功能。本文将详细介绍…

kubernetes》》k8s》》删除命名空间

使用 kubectl delete ns 命名空间 --force --grace-period0 如果还删除不掉 需要 kubectl get namespace 命名空间 -o json > x.json vim x.json kubectl replace --raw “/api/v1/namespaces/命名空间/finalize” -f ./x.json

玩转Docker | 使用Docker部署DashMachine个人书签工具

玩转Docker | 使用Docker部署DashMachine个人书签工具 前言一、DashMachine介绍DashMachine简介DashMachine使用场景二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署DashMachine服务下载镜像创建容器创建容器检查容器状态检查服务端口安全设置四、访问Das…

SQL进阶知识:一、高级查询

今天介绍下关于高级查询的详细介绍&#xff0c;包括子查询、连接查询、分组查询等&#xff0c;并结合MySQL数据库提供实际例子。 一、子查询&#xff08;Subqueries&#xff09; 子查询是嵌套在另一个查询中的查询语句&#xff0c;通常用于提供条件过滤、生成临时数据集等。子…

【Git】Git Revert 命令详解

Git Revert 命令详解 1. Git Revert 的基本概念 Git Revert 是一个用于撤销特定提交的命令。与 Git Reset 不同&#xff0c;Git Revert 不会更改提交历史&#xff0c;而是会创建一个新的提交来撤销指定提交的更改。这意味着&#xff0c;使用 Git Revert 后&#xff0c;项目的…

华为S系列交换机CPU占用率高问题排查与解决方案

问题概述 在华为S系列交换机(V100&V200版本)运行过程中&#xff0c;CPU占用率过高是一个常见问题&#xff0c;可能导致设备性能下降甚至业务中断。根据华为官方维护宝典&#xff0c;导致CPU占用率高的主要原因可分为四大类&#xff1a;网络攻击、网络震荡、网络环路和硬件…