zoukankan      html  css  js  c++  java
  • 内存泄漏场景

    再推一次老貘的《Go101》。

    子字符串造成的内存泄露

    var s0 string // 一个包级变量
    
    // 一个演示目的函数。
    func f(s1 string) {
        s0 = s1[:50]
        // 目前,s0和s1共享着承载它们的字节序列的同一个内存块。
        // 虽然s1到这里已经不再被使用了,但是s0仍然在使用中,
        // 所以它们共享的内存块将不会被回收。虽然此内存块中
        // 只有50字节被真正使用,而其它字节却无法再被使用。
    }
    
    func demo() {
        s := createStringWithLengthOnHeap(1 << 20) // 1M bytes
        f(s)
    }

    其实我们可以先将s1这个内存块转移一下,这样gc就会自动标记清理。

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

    这个方法的缺点就是多了两次“复制”

    func f(s1 string) {
        s0 = (" " + s1[:50])[1:]
    }

    这个就是利用编译器的优化了,具体可以看下ssa,涉及编译器优化,就不要作为首选了。

    import "strings"
    
    func f(s1 string) {
        var b strings.Builder
        b.Grow(50)
        b.WriteString(s1[:50])
        s0 = b.String()
    }

    这是一个看起来复杂,但却是最合理的思路。

    上面讲了最合理的思路,但并不是最合理的方式,因为这种代码有坏的味道,现在golang提供了repeat支持,这是首选。

    s0 = strings.Repeat(s1, 50)

    可见实现思路还是一样的

    func Repeat(s string, count int) string {
        if count == 0 {
            return ""
        }
    
        // Since we cannot return an error on overflow,
        // we should panic if the repeat will generate
        // an overflow.
        // See Issue golang.org/issue/16237
        if count < 0 {
            panic("strings: negative Repeat count")
        } else if len(s)*count/count != len(s) {
            panic("strings: Repeat count causes overflow")
        }
    
        n := len(s) * count
        var b Builder
        b.Grow(n)
        b.WriteString(s)
        for b.Len() < n {
            if b.Len() <= n/2 {
                b.WriteString(b.String())
            } else {
                b.WriteString(b.String()[:n-b.Len()])
                break
            }
        }
        return b.String()
    }

    子切片造成的内存泄露

    需要了解切片的源码

    var s0 []int
    
    func g(s1 []int) {
        // 假设s1的长度远大于30。
        s0 = s1[len(s1)-30:]
    }

    这里我们可以用append来处理,这样就不会公用一个底层了

    func g(s1 []int) {
        s0 = append(s1[:0:0], s1[len(s1)-30:]...)
    }

    因为未重置丢失的切片元素中的指针而造成的临时性内存泄露

    func h() []*int {
        s := []*int{new(int), new(int), new(int), new(int)}
        // 使用此s切片 ...
    
        return s[1:3:3]
    }

    其实就是一个删除元素,但是由于是指针类型,我们需要置nil的

    func h() []*int {
        s := []*int{new(int), new(int), new(int), new(int)}
    
        s[0], s[len(s)-1] = nil, nil // 重置首尾元素指针
        return s[1:3:3]
    }

    因为协程被永久阻塞而造成的永久性内存泄露

    这个Dave Cheney说过,尽量少用协程,信道,为什么?你真的需要吗?如果你写的不能百分百掌控,这些都是埋雷,Golang新手偏爱协程,但是协程我个人认为你需要把调度和运行,channel和goroutine源码看懂再写,否则是有很大风险的。

    因为没有停止不再使用的time.Ticker值而造成的永久性内存泄露

    当一个time.Timer值不再被使用,一段时间后它将被自动垃圾回收掉。 但对于一个不再使用的time.Ticker值,我们必须调用它的Stop方法结束它,否则它将永远不会得到回收。

    因为不正确地使用终结器(finalizer)而造成的永久性内存泄露

    我坚决反对新手使用这个。

    延迟调用函数导致的临时性内存泄露

    for里使用defer

    我认为sync.pool的源码是要学习的,否则你只是在背代码。

    https://github.com/golang/go/pull/32138

    https://cbsheng.github.io/posts/fasthttp%E6%BA%90%E7%A0%81%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E5%88%86%E6%9E%90/

    https://www.zhihu.com/question/327580797

    https://xargin.com/logic-of-slice-memory-leak/

    https://github.com/golang/go/issues/20138

    https://qcrao.com/2020/05/06/dive-into-go-sync-map/

     
    一个没有高级趣味的人。 email:hushui502@gmail.com
  • 相关阅读:
    已知前序中序后序中的两种,如何求另一种
    sql(上)例题
    sql语句顺序/包含执行顺序和书写顺序
    VScode中文乱码问题
    好用的后台管理模板Bootstrap3
    手把手教你用Typora自动上传到picgo图床【教程与排坑】
    解决GitHub网页githubusercontent地址无法访问问题
    Java学习+面试指南(转)
    Linux C/C++ 方向学习经验【转】
    Scrapy学习笔记(一)
  • 原文地址:https://www.cnblogs.com/CherryTab/p/12848811.html
Copyright © 2011-2022 走看看