Golang中可能导致内存泄漏的情况

1.闭包导致的内存泄漏,在内部函数中使用外部函数的值,导致该变量由栈空间逃逸到堆空间

2.子字符串导致:

var s0 string// package-level变量
func f(s string){
    s0 = s[:50]
    // s0与s共享相同的底层内存块。
    // 虽然s现在不是活动的,但是s0仍然是活动的
    // 所以无法收集它们共享的内存块,虽然这个块中只使用了50个字节
    // 并且这个块中的所有其他字节都不可用。
}
func demo(){
    s :=createStringWithLengthOnHeap(1<<20)// 1M bytes
    f(s)
}

为了避免这种类型的内存泄漏,可以将子字符串转换为[]字节值,然后将[]字节值转换回字符串。

func f(s string){
    s0 =string([]byte(s[:50]))
}

这段代码会导致1M空间的内存泄漏,在获取子字符串时应该避免使用这种直接切的方式进行获取,除了上述方法外,在Go1.12(将于2019年初发布)开始,可以在strings标准包中调用Repeat函数来克隆字符串。

3.切片引起:

和子字符串导致的内存泄漏相似

var s0 []int
func g(s1 []int){
    // 假设s1的长度远大于30
    s0 = s1[len(s1)-30:]
}
调用此方法后,可能导致s1的前30个值无法被回收,进而引起内存泄漏
如果我们想避免这种类型的内存泄漏,我们必须为s0复制30个元素,这样存活的s0就不会妨碍存储s1元素的内存块被收集。

func g(s1 []int){
    s0 =append([]int(nil), s1[len(s1)-30:]...)
    // 现在如果没有其他值引用内存块,那么可以收集包含s1元素的内存块。
}

还有一种情况是切片元素是指针

func h()[]*int{
    s :=[]*int{new(int),new(int),new(int),new(int)}
    return s[1:3]
    
    // 对s进行手动释放操作...
    s[0], s[len(s)-1]=nil,nil
}

在上述情况中,可能引起指针指向的对象永远无法被释放,因此需要代码去手动释放

4.挂起Goroutines导致的内存泄漏:

有时Go程序中的一些goroutine可能永远处于阻塞状态。这样的goroutines叫做挂起的goroutine。Go运行时不会杀死挂起的goroutines,因此分配给挂起goroutines的资源(以及所引用的内存块)永远不会被垃圾回收。

5.time.Ticker导致的内存泄漏:

当一个time.Timer值不再使用,一段时间后将被垃圾回收。但对于time.Ticker值并非如此。应该直接停止它当其不再使用的时候。

6.不正确地使用终结器(Finalizers)导致的内存泄漏(不太理解)

posted @ 2020-10-28 17:49  LeeJuly  阅读(737)  评论(0编辑  收藏  举报