云海的博客
首页
  • 接口
  • 数组
  • slice
  • map
  • 指针
  • 反射
  • Context
  • sync.map
  • 锁
  • 类型和类型指针分析
  • recover
  • 从零实现RPC框架
  • make和new区别
  • channel
  • sync.Once
  • sync.Pool
  • protobuf
  • MongoDB pkg源码-findone
  • MyBatis
  • Maven
  • 解析Laravel框架—路由处理
  • PHP(客户端)与 Golang(服务端)使用grpc+protobuf 通信
  • JAVA(客户端)与 Golang(服务端) 使用grpc+protobuf通信
  • Docker使用笔记-常用命令
  • Docker使用笔记-容器间通讯
  • Docker使用笔记-搭建Redis集群
  • Docker使用笔记-镜像多阶段构建
  • Kubernetes部署golang服务
  • Linux常用命令
  • Docker安装Prometheus与Grafana
  • Protobuf
  • TCP抓包
  • 概述-《TCP/IP详解》读书笔记
  • 索引
  • 事务隔离级别
  • 常识
  • 每日一题(1)
  • 每日一题(2)
  • 每日一题(3)
  • 每日一题(4)
关于
GitHub (opens new window)

云海

服务端研发
首页
  • 接口
  • 数组
  • slice
  • map
  • 指针
  • 反射
  • Context
  • sync.map
  • 锁
  • 类型和类型指针分析
  • recover
  • 从零实现RPC框架
  • make和new区别
  • channel
  • sync.Once
  • sync.Pool
  • protobuf
  • MongoDB pkg源码-findone
  • MyBatis
  • Maven
  • 解析Laravel框架—路由处理
  • PHP(客户端)与 Golang(服务端)使用grpc+protobuf 通信
  • JAVA(客户端)与 Golang(服务端) 使用grpc+protobuf通信
  • Docker使用笔记-常用命令
  • Docker使用笔记-容器间通讯
  • Docker使用笔记-搭建Redis集群
  • Docker使用笔记-镜像多阶段构建
  • Kubernetes部署golang服务
  • Linux常用命令
  • Docker安装Prometheus与Grafana
  • Protobuf
  • TCP抓包
  • 概述-《TCP/IP详解》读书笔记
  • 索引
  • 事务隔离级别
  • 常识
  • 每日一题(1)
  • 每日一题(2)
  • 每日一题(3)
  • 每日一题(4)
关于
GitHub (opens new window)
  • 接口
  • 数组
  • slice
  • map
  • 反射
  • sync.Pool
    • net包笔记
    • net-rpc分析
    • 指针
    • 数组排序
    • Context
    • sync.map
    • 锁
    • recover
    • 泛型
    • 类型和类型指针分析
    • make和new区别
    • channel
    • sync.Once
    • protobuf
    • GoLand debug(1)
    • 从零实现RPC框架
    • 从零开始学Go Origin框架
    • MongoDB pkg源码-findone
    • Golang
    云海
    2023-06-03
    目录

    sync.Pool

    # 介绍

    sync.Pool 数据类型用来保存一组可独立访问的临时对象。请注意这里加粗的“临时”这两个字,它说明了sync.Pool这个数据类型的特点,也就是说,它池化的对象会在未来的某个时候被毫无预兆的移除掉。 因为Pool可以有效的减少新对象的申请,从而提高程序性能,所以Go内部库也用到了sync.Pool,比如fmt包,它会使用一个动态大小的buffer池做输出缓存。

    • 1.sync.Pool 本身就是线程安全的,多个goroutine可以并发地调用它的方法存储对象;
    • 2.sync.Pool 不可以在使用之后再复制使用。

    # sync.Pool的使用方法

    提供了三个对外的方法:New、Get和Put。

    • 1.New Pool struct包含一个New字段,这个字段的类型是函数func() interface{}。当调用Pool的Get方法从池中获取元素,没有更多的空闲元素可返回时,就会调用这个New方法来创建新的元素。如果你没有设置New字段,没有更多的空间元素可返回时,Get方法将返回nil,表明当前没有可用的元素。
    • 2.Get 如果调用这个方法,就会从Pool取走一个元素,这也就意味着,这个元素会从Pool中移除,返回给调用者。不过,除了返回值是正常实例化的元素,Get方法的返回值还可能会是一个nil(Pool.New字段没有设置,又没有空闲元素可以返回),所以你在使用的时候,可能需要判断。
    • 3.Put 这个方法用于将一个元素返还给Pool,Pool会把这个元素保存到池中,并且可以复用。但如果Put一个nil值,Pool就会忽略这个值。

    下面我们看看sync.Pool最常用的一个场景:buffer池(缓冲池)。

    var buffers = sync.Pool{
    	New: func() interface{} {
    		return new(bytes.Buffer)
    	},
    }
    
    func GetBuffer() *bytes.Buffer {
    	return buffers.Get().(*bytes.Buffer)
    }
    
    func PutBuffer(buf *bytes.Buffer) {
    	buf.Reset()
    	buffers.Put(buf)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    注意点,内存泄漏和内存浪费。

    • 内存泄漏:取出来的bytes.Buffer在使用的时候,我们可以往这个元素中增加大量的byte数据,这会导致底层的byte slice的容量可能会变得很大。这个时候,即使Reset再放回到池子中,这些byte slice的容量不会改变,所占的空间依然很大。而且,因为Pool回收的机制,这些大的Buffer可能不被回收,而是一直占用很大的空间,这属于内存泄漏问题。 解决方法:元素在放回时,增加了检查逻辑,改成放回的超过一定大小的buffer,就直接丢弃掉,不再放入池子中。
    • 内存浪费:池子中的buffer都比较大,但在实际使用的时候,很多时候只需要一个小的buffer,这也是一种浪费现象。 解决方法:可以将buffer池分成几层。
    var (
      bufioWriter2kPool sync.Pool
      bufioWriter4kPool sync.Pool
    )
    
    func bufioWriterPool(size int) *sync.Pool {
      switch size {
      case 2 << 10:
        return &bufioWriter2kPool
      }
      case 4 << 10:
        return &bufioWriter4kPool
      return nil
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    上次更新: 2023/06/05
    反射
    net包笔记

    ← 反射 net包笔记→

    最近更新
    01
    函数
    04-11
    02
    面试题
    04-11
    03
    EFK日志收集系统单机版
    08-18
    更多文章>
    Theme by Vdoing | Copyright © 2022-2025 Evan Xu | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式