分享一个自己 golang 的库,用于尽量不 GC 的内存池

304 天前
 matytan

实现内存池用于对切片对象进行复用,减少内存分配次数 Mempool 是一个内存池库,用于在处理字节缓冲区时减少分配并提高性能。 https://github.com/matyle/mempool

2754 次点击
所在节点    Go 编程语言
28 条回复
alexsunxl
304 天前
buffer 的内存池。
没点进去之前我以为是变量的内存池,哈哈哈
vfs
304 天前
提高性能? 应该数据说一下。
Mohanson
304 天前
为什么不用标准库里的 sync.Pool ... ...
zdt3476
304 天前
看了下,只是 bytes.Buffer 的内存池,用途感觉不是很广; 而且也没有和 sync.Pool 对比的 benchmark
matytan
304 天前
@vfs 嗯嗯准备做个 benchmark ,已经在业务中体现了,还没做 benchmark
matytan
304 天前
@zdt3476 嗯嗯 用途不是很广,主要是防止多协程频繁 GC ,会做个 benchmark ,README 没有贴上我业务内存的对比图
matytan
304 天前
@Mohanson 最开始业务里面就是用的 sync.pool 但是在大内存业务下,gc 会撑爆内存,后面使用了这个限制 gc
kkhaike
304 天前
。。。解释下,为啥用了 sync.pool ,gc 会撑爆内存。。。
matytan
304 天前
@kkhaike 本质上是 json 的序列化导致 makeslice 很大,使用 syncpool 并没有限制协程的 buffer 对象,来不及 gc 导致某一时刻峰值很大
hopingtop
304 天前
我们也遇到 op 一样的问题,json 序列化会撑爆内存,就是因为 sync.Pool + json.Buffer 导致的。
当 99%的数据是小包 1%的数据突然来一个几十 MB 的大包,那么有可能后面 sync.Pool 里面的 buffer 都会变成几十 MB ,就会导致内存爆掉。

这个问题,Golang 有最新的提案和实现, 就是动态优化 buffer 的大小。 但是还没有合并!

相关 code 链接 https://go-review.googlesource.com/c/go/+/471200
Trim21
304 天前
@hopingtop https://github.com/valyala/bytebufferpool 可以用 fasthttp 作者写的这个,如果少数 buffer 特别大会直接丢掉对应的 buffer 。
hopingtop
304 天前
我才看了 op 的代码,如果你真的想实现 mem 的高效利用,可以参考上面的链接实现或许会更好!

目前 mempool 有点问题就是,限制了上限,但是释放不了下限,没有考虑到 release 机制, 最终还是可能会把所有 buffer 都撑大!
hopingtop
304 天前
@Trim21 #11 场景比较特殊,当前绑定了 json 序列化,后期准备改成 pb 一劳永逸
Nazz
304 天前
@hopingtop 包装下 sync.Pool, 把大包扔掉就好了
hopingtop
304 天前
@Nazz #14 唉,encode/json 底层实现用了 全局 sync.Pool ,包括 json-iterator 也是类似实现,所以包不了。 如果 json-iterator 提供设置 自定义的 pool 就好了,可惜也没有
Nazz
304 天前
@hopingtop 使用 json.Encoder/Decoder 就可以自己管理 buffer pool 了
hopingtop
304 天前
@Nazz #16 我确定是我表示不清楚,还是太久了,你忘记了,所以我们说的不是同一个东西,你可以再去看看代码
```go
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w, escapeHTML: true}
}

// Encode writes the JSON encoding of v to the stream,
// followed by a newline character.
//
// See the documentation for Marshal for details about the
// conversion of Go values to JSON.
func (enc *Encoder) Encode(v any) error {
if enc.err != nil {
return enc.err
}

e := newEncodeState()
defer encodeStatePool.Put(e)
```
```go
var encodeStatePool sync.Pool

func newEncodeState() *encodeState {
if v := encodeStatePool.Get(); v != nil {
e := v.(*encodeState)
e.Reset()
if len(e.ptrSeen) > 0 {
panic("ptrEncoder.encode should have emptied ptrSeen via defers")
}
e.ptrLevel = 0
return e
}
return &encodeState{ptrSeen: make(map[any]struct{})}
}
```
核心消耗内存的地方是 encodeState
hopingtop
304 天前
@hopingtop #17 '我确定是' -> '我不确定是'
Nazz
304 天前
@hopingtop 是我想得太简单了😂
matrix1010
303 天前
不懂为什么会爆,sync pool 既然是循环使用的那 pool 里面的对象数量应该不会很多才对。考虑到 json 的普遍性如果真有问题应该有很多相关 issues 才对,能不能放几个

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/956136

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX