Go 并发编程实战课
晁岳攀(鸟窝)
前微博技术专家,知名微服务框架 rpcx 作者
25635 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 22 讲
Go 并发编程实战课
15
15
1.0x
00:00/00:00
登录|注册

10 | Pool:性能提升大杀器

内存浪费
内存泄漏
Put方法
Get方法
Put
Get
New
应用场景
常用的Worker Pool库
Memcached Client连接池
数据库连接池
TCP连接池
buffer池
坑
实现原理
使用方法
临时对象存储
对象池的重要性
Go语言的自动垃圾回收
思考题
Worker Pool
第三方库
sync.Pool
性能提升大杀器
Pool

该思维导图由 AI 生成,仅供参考

你好,我是鸟窝。
Go 是一个自动垃圾回收的编程语言,采用三色并发标记算法标记对象并回收。和其它没有自动垃圾回收的编程语言不同,使用 Go 语言创建对象的时候,我们没有回收 / 释放的心理负担,想用就用,想创建就创建。
但是,如果你想使用 Go 开发一个高性能的应用程序的话,就必须考虑垃圾回收给性能带来的影响,毕竟,Go 的自动垃圾回收机制还是有一个 STW(stop-the-world,程序暂停)的时间,而且,大量地创建在堆上的对象,也会影响垃圾回收标记的时间。
所以,一般我们做性能优化的时候,会采用对象池的方式,把不用的对象回收起来,避免被垃圾回收掉,这样使用的时候就不必在堆上重新创建了。
不止如此,像数据库连接、TCP 的长连接,这些连接在创建的时候是一个非常耗时的操作。如果每次都创建一个新的连接对象,耗时较长,很可能整个业务的大部分耗时都花在了创建连接上。
所以,如果我们能把这些连接保存下来,避免每次使用的时候都重新创建,不仅可以大大减少业务的耗时,还能提高应用程序的整体性能。
Go 标准库中提供了一个通用的 Pool 数据结构,也就是 sync.Pool,我们使用它可以创建池化的对象。这节课我会详细给你介绍一下 sync.Pool 的使用方法、实现原理以及常见的坑,帮助你全方位地掌握标准库的 Pool。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言中的sync.Pool是一个性能提升的重要工具,通过对象池的方式回收不再使用的对象,避免重新创建,从而避免了垃圾回收对性能的影响。在Go 1.13中,sync.Pool经过优化,避免了使用锁,提高了性能。除了sync.Pool,文章还介绍了buffer池的使用场景和实现原理。同时,文章还提到了使用sync.Pool的注意事项,以及其它类型的Pool,如TCP连接池、数据库连接池等。此外,文章还介绍了Worker Pool的应用场景,帮助读者全方位地掌握标准库的Pool。总之,sync.Pool是一个强大的工具,能够有效提升Go语言程序的性能。文章还介绍了一些第三方库提供的buffer池和连接池,以及它们的特点和适用场景。另外,文章还提到了在分布式系统或者微服务框架中,可能会有大量的并发Client请求,如果Client的耗时占比很大,可以考虑池化Client,以便重用。如果系统中的goroutine数量非常多,程序的内存资源占用比较大,而且整体系统的耗时和GC也比较高,可以通过Worker Pool解决大量goroutine的问题,从而降低这些指标。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 并发编程实战课》
,新⼈⾸单¥59
立即购买
登录 后留言

全部留言(16)

  • 最新
  • 精选
  • 末班车
    之前用到去看过,好像是通过一个链表的形式,把request存起来,最新的在链表的头,最旧的在链表的尾部,可是不懂的是,为什么每次取出了req,还要重新赋零值呢,这和我每次new一个有什么区别么?求大佬指点。

    作者回复: 好问题。 重新赋零值相当于reset,避免先前的垃圾数据影响这个request。 new会在堆上新创建一个对象。

    2020-11-02
    3
    27
  • 那时刻
    请问老师, sync.Pool会有内存泄漏,怎么理解因为 Pool 回收的机制,这些大的 Buffer 可能不被回收?

    作者回复: 对

    2020-11-03
    6
    4
  • lesserror
    老师的文章讲解的非常细致。 请问一下: 1. 三色并发标记算法 这个 链接地址 Not found 了。 2. 举例大的buffer 不被回收的 第一个 源码 函数:putEncodeState ,我没找到。请问一下在哪个文件里面呀。

    作者回复: 第一个已修正,go官方网站域名改了。 第二个看文章的贴图

    2021-08-21
    1
  • 冰糕不冰
    讲的太好了! 真的是开启了新世界的大门。 感谢大佬

    作者回复: 谢谢点击[http://img01.sogoucdn.com/app/a/200678/64d6120c9ab37cb8ce7f52cea0ec9f75.gif]查看表情

    2022-03-26
  • tingting
    请问老师,RPC request 池化的实现为什么不用sync.Map,而是选择使用链表实现呢?

    作者回复: 链表更简单;map获取删除相对复杂;sync.pool会自动回收,不受控

    2022-01-24
  • 授人以🐟,不如授人以渔
    「因为 Pool 回收的机制,这些大的 Buffer 可能不被回收」是什么原因?

    作者回复: 因为每次是部分回收

    2021-11-04
  • Junes
    分享一下我的理解,主要分为回收和获取两个函数: func (server *Server) freeRequest(req *Request) { server.reqLock.Lock() // 将req放在freeReq的头部,指向原先的链表头 // 至于为什么放在头部、而不是尾部,我觉得是放在尾部需要遍历完这个链表(增加时间复杂度)、或者要额外维护一个尾部Request的指针(增加空间复杂度),权衡下放在头部更方便 req.next = server.freeReq server.freeReq = req server.reqLock.Unlock() } func (server *Server) getRequest() *Request { server.reqLock.Lock() // freeReq是一个链表,保存空闲的Request req := server.freeReq if req == nil { // 初始状态:freeReq为空时,在heap上重新分配一个对象 req = new(Request) } else { server.freeReq = req.next // 复用的关键在这里,这里并不是新建一个对象 new(Request) // 这里的思想类似于Reset,将原先有数据的Request设置为空 *req = Request{} } server.reqLock.Unlock() return req }
    2020-11-02
    1
    13
  • Yayu
    谢谢老师,喜欢老师这篇文章中通过外链的方式列出一些老师常用的三方库,很有用!
    2020-11-03
    8
  • 大布丁
    一文解决之前组长问我的一个问题:除了使用更多的goroutine来干更多的活之外,还有什么设计与优化的思想?当时没往线程池跟sync.Pool去思考,现在感触很深了!身为年轻人,代码功底不足的情况,我还是喜欢阅读源码后,动手实现里面的几个方法,以便于自己能够更深刻的去理解第三方库!
    2022-03-20
    1
  • 虫子樱桃
    思考题的奥秘感觉在这两个函数 ``` // ServeRequest is like ServeCodec but synchronously serves a single request. // It does not close the codec upon completion. func (server *Server) ServeRequest(codec ServerCodec) error { sending := new(sync.Mutex) service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec) if err != nil { if !keepReading { return err } // send a response if we actually managed to read a header. if req != nil { server.sendResponse(sending, req, invalidRequest, codec, err.Error()) server.freeRequest(req) } return err } service.call(server, sending, nil, mtype, req, argv, replyv, codec) return nil } func (server *Server) freeRequest(req *Request) { server.reqLock.Lock() req.next = server.freeReq server.freeReq = req server.reqLock.Unlock() } ```
    2020-11-02
    2
    1
收起评论
显示
设置
留言
16
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部