Memory Go中处理包分配的最佳实践
我正在写一个包,它大量使用内部缓冲区进行临时存储。我有一个全局(但不导出)字节片,从1024个元素开始,根据需要加倍增长 然而,我的包的用户很可能会以这样一种方式使用它,导致分配了一个大的缓冲区,但随后停止使用包,从而浪费了大量分配的堆空间,我也无法知道是否要释放缓冲区(或者,既然这样做了,就让它被GC’d) 我想到了三种可能的解决方案,但没有一种是理想的。我的问题是:在这样的情况下,这些解决方案中有没有一个是标准做法,或者我没有想到的?有没有标准的做法?还有其他想法吗Memory Go中处理包分配的最佳实践,memory,go,package,allocation,Memory,Go,Package,Allocation,我正在写一个包,它大量使用内部缓冲区进行临时存储。我有一个全局(但不导出)字节片,从1024个元素开始,根据需要加倍增长 然而,我的包的用户很可能会以这样一种方式使用它,导致分配了一个大的缓冲区,但随后停止使用包,从而浪费了大量分配的堆空间,我也无法知道是否要释放缓冲区(或者,既然这样做了,就让它被GC’d) 我想到了三种可能的解决方案,但没有一种是理想的。我的问题是:在这样的情况下,这些解决方案中有没有一个是标准做法,或者我没有想到的?有没有标准的做法?还有其他想法吗 去他妈的 哦,好吧。处理
注意:您可以看到现有的项目(相关的代码只有几十行)。一种常见的方法是让客户端将现有的[]字节(或任何内容)作为参数传递给某个调用/函数/方法。例如:
// The returned slice may be a sub-slice of dst if dst was large enough
// to hold the entire encoded block. Otherwise, a newly allocated slice
// will be returned. It is valid to pass a nil dst.
func Foo(dst []byte, whatever Bar) (ret []byte, err error)
()
另一种方法是从中获取一个新的[]字节,例如和/或(如果您更喜欢该概念的后一个名称),并依靠客户端将使用过的缓冲区返回到此类“回收站”
顺便说一句:你这么想是对的。如果可以合理地重用[]字节缓冲区,则有可能降低GC负载,从而使程序性能更好。有时,差异可能是至关重要的
我有一个全局(但没有导出)字节片,我从它开始
拥有1024个元素,并根据需要加倍增长
这就是你的问题。你不应该在你的包里有这样一个全局的
通常,最好的方法是使用带有附加函数的导出结构。缓冲区应驻留在此未报告的结构中。这样,用户就可以实例化它,让垃圾收集器在释放它时清理它
您还希望避免像这样要求全局变量,因为它会妨碍单元测试。单元测试应该能够像用户一样实例化导出的结构,并在每次测试中都这样做
另外,根据您需要的缓冲区类型,
bytes.buffer
可能很有用,因为它已经提供了io.Reader
和io.Writer
功能<代码>字节。缓冲区也会自动增大和缩小其缓冲区。在中,您将看到对b.Truncate(0)
的各种调用,该调用使用注释“reset to recover space”(重置以恢复空间)进行收缩。您可以在每次操作结束时重新释放缓冲区
buffer = buffer[:0]
然后,如果函数extendAndSliceBuffer
需要增长,则它很可能拥有原始的备份数组。否则,您将面临一个新的分配,当您执行extendAndSliceBuffer
时,您可能会得到一个新的分配
总的来说,我认为一个更干净的解决方案是像@jnml所说的那样,让用户传递他们自己的缓冲区,如果他们关心性能的话。如果他们不关心性能,那么您不应该使用全局变量,只需根据需要分配缓冲区,并在缓冲区超出范围时释放缓冲区。编写非线程安全的go代码通常是非常糟糕的。如果两个不同的goroutine同时调用修改缓冲区的函数,谁知道当它们完成时缓冲区将处于什么状态?如果用户认为分配性能是一个瓶颈,就让他们提供一个临时空间缓冲区。这些缓冲区是用户从未接触过的。该包实现了许多方便的功能,缓冲区用于临时存储。“我不认为这不是你所想的设计模式。”joshlf13我的建议仍然有效,不管缓冲区的类型如何。不要使用全局函数,给用户一些可以保留的东西,以便使用垃圾收集器。如果我想让用户处理更复杂的界面,我不妨使用导出“立即释放内存”函数的方法。密码。它应该能让你更好地理解我的意思。@joshlf13为什么不直接使用bytes.Buffer呢?我认为“传递你自己的缓冲区”模式在这里不起作用,因为它完全是内部的(用户永远看不到缓冲区的内容)。不过,我会更深入地研究缓存/池;这些看起来很有希望。