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
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部