Golang 防内存泄漏编码原则
(给Go开发大全
加星标)
来源:zmlgo
https://www.cnblogs.com/share-ideas/p/11365511.html
【导读】go 编程中,如果写法不当,可能引发内存泄漏。本文介绍了一些 coding 原则和详细示例。
1、给系统打压力,内存占用增大,但停止打压后,内存不能降低,则可能有内存泄漏。
2、top不能实时反映程序占用内存,因Go向系统申请内存不使用后,并不立即归还系统。
3、程序占用系统内存、Go的堆内存、实际使用内存:从系统申请的内存会在Go的内存池管理,整块的内存页,长时间不被访问并满足一定条件后,才归还给操作系统。又因为有GC,堆内存也不能代表内存占用,清理过之后剩下的,才是实际使用的内存。
4、调用runtime.ReadMemStats可以看到Go的内存使用信息。
5、使用go tool pprof -inuse_space http://127.0.0.1:6060/debug/pprof/heap?debug=2得到更细信息,其中HeapInuse为实际内存使用量。
6、第一条原则是,绝对不能由消费者关channel,因为向关闭的channel写数据会panic。正确的姿势是生产者写完所有数据后,关闭channel,消费者负责消费完channel里面的全部数据。
func produce(ch chan<- T) {
defer close(ch) // 生产者写完数据关闭channel
ch <- T{}
}
func consume(ch <-chan T) {
for _ = range ch { // 消费者用for-range读完里面所有数据
}
}
ch := make(chan T)
go produce(ch)
consume(ch)
7、第二条原则是,利用关闭channel来广播取消动作,并可配合着WaitGroup类来使用。
func produce(ch chan<- T, cancel chan struct{}) {
select {
case ch <- T{}:
case <- cancel: // 用select同时监听cancel动作
}
}
func consume(ch <-chan T, cancel chan struct{}) {
v := <-ch
err := doSomeThing(v)
if err != nil {
close(cancel) // 能够通知所有produce退出
return
}
}
for i:=0; i<10; i++ {
go produce()
}
consume()
8、通过chann发信号来关闭协程。
func (m *TtlMap) clear() {
for {
select {
// 关闭
case <-m.stop:
return
//定期清理...
}
}
}
9、MapWarpper作为局部变量时,定义它的函数结束后,MapWarpper的生命周期已结束,Gc会将其回收。Gc回收MapWarpper时执行了onGarbageCollect()函数,将Ttlmap的clear协程关闭,进而将Ttlmap回收。
strcut TtlMap {
...
stop chan bool
}
// 包裹定义
struct MapWarpper {
*TtlMap
}
func New() *MapWarpper {
map := &TtlMap{
...
}
go map.clear()
// 包一层
mw := &MapWarpper{map}
// 重点在此:设置被回收时操作
runtime.SetFinalizer(mw, onGarbageCollect)
return mw
}
10、通过context包来避免内存泄漏。
func main() {
ctx, cancel := context.WithCancel(context.Background())
ch := func(ctx context.Context) <-chan int {
ch := make(chan int)
go func() {
for i := 0; ; i++ {
select {
case <- ctx.Done():
return
case ch <- i:
}
}
} ()
return ch
}(ctx)
for v := range ch {
fmt.Println(v)
if v == 5 {
cancel()
break
}
}
}
- EOF -
如果觉得本文不错,欢迎转发推荐给更多人。
分享、点赞和在看
支持我们分享更多好文章,谢谢!