Golang笔记

Golang 减小gc 压力、避免内存泄漏小tips

减少对象分配 所谓减少对象的分配,实际上是尽量做到,对象的重用。 比如像如下的两个函数定义:

func(r*Reader)Read()([]byte,error)
func(r*Reader)Read(buf[]byte)(int,error)

第一个函数没有形参,每次调用的时候返回一个 []byte ,第二个函数在每次调用的时候,形参是一个 buf []byte 类型的对象,之后返回读入的byte的数目。

第一个函数在每次调用的时候都会分配一段空间,这会给 gc 造成额外的压力。第二个函数在每次调用的时候,会重用形参声明。

老生常谈 string[]byte 转化,在 stirng[]byte 之间进行转换,会给 gc 造成压力 通过 gdb ,可以先对比下两者的数据结构:

type = struct []uint8 {    uint8 *array;    int len;    int cap;}
type = struct string {    uint8 *str;    int len;}

两者发生转换的时候,底层数据结结构会进行复制,因此导致 gc 效率会变低。解决策略上,一种方式是一直使用 []byte ,特别是在数据传输方面,[]byte 中也包含着许多 string 会常用到的有效的操作。另一种是使用更为底层的操作直接进行转化,避免复制行为的发生,使用 unsafe.Pointer 直接进行转化。

尽少使用 + 连接 string 由于采用 + 来进行 string 的连接会生成新的对象,降低 gc 的效率,好的方式是通过 append 函数来进行。

但是还有一个弊端,比如参考如下代码:

b := make([]int, 1024) b = append(b, 99) fmt.Println("len:", len(b), "cap:", cap(b))

在使用了append操作之后,数组的空间由 1024 增长到了 1312 ,所以如果能提前知道数组的长度的话,最好在最初分配空间的时候就做好空间规划操作,会增加一些代码管理的成本,同时也会降低 gc 的压力,提升代码的效率。

对上面的代码可以这样改进:

b := make([]int, 0, 1024)

内存泄漏大部分原因是代码里新开的协程有引用地方,导致GC无法释放。

避免内存泄漏的两个原则

1、 绝对不能由消费者关 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)

为什么 consume 要读完 channel 里面所有数据?因为 go produce()可能有多个,这样写的代码,在读完ch可以确定所有produce的goroutine都退出了,不会泄漏。

2、 利用关闭channel来广播取消动作。向关闭的channel读数据永远不会阻塞,这是进阶的技巧。假设消费者拿到数据处理后有error发生,整个动作失败,那么需要有某种机制通知生产者停止并退出。

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()

WaitGroup之类的可以配合着用,看自己喜欢的风格。基本上能处理好error场景下的资源释放,问题就不大。 对于并发的代码心存敬畏之心,哪怕用Go,哪怕有channel这么好用的东西!

相关参考:

本文网址: https://golangnote.com/topic/222.html (转载注明出处)
关于GolangNote:记录在工作中使用golang 遇到、面临的相关问题及解决方法。如果你在这里获得一些知识或信息,解决你的编程问题,请考虑捐赠给不幸的人或者你喜欢的慈善机构,除捐赠外,种植树木、志愿服务或减少排碳的行为也很有益处。如果你有任何问题可以在下面 留言
Be the first to comment!
Captcha image
Relative Articles