go语言基础学习七

GMP能不能去掉P层?会怎么样#

每个 P 有自己的本地队列,大幅度的减轻了对全局队列的直接依赖,所带来的效果就是锁竞争的减少。而 GM 模型的性能开销大头就是锁竞争。

每个 P 相对的平衡上,在 GMP 模型中也实现了 Work Stealing 算法,如果 P 的本地队列为空,则会从全局队列或其他 P 的本地队列中窃取可运行的 G 来运行,减少空转,提高了资源利用率。

goroutine什么情况会发生内存泄漏?如何避免#

goroutine 泄漏 4 种场景#

chan 阻塞永久等待#

G 阻塞读 / 写无配套另一端收发,goroutine 永久挂起不退出



ch := make(chan int)
<-ch // 没人写,G卡死

无限 for 死循环无退出条件#

for{}里面无 break、无退出信号,持续常驻

全局变量持有 goroutine 引用#

全局切片 /map 存 G 内大对象,GC 无法回收 G。 全局slice/map是常驻内存,保存了 G 里创建的大结构体 / 变量: G 执行完本该消亡,但全局变量还握着它的引用 → GC 不能回收这块内存,G 资源常驻 = 内存泄漏。



var global []*BigData // 全局
func test(){
    go func(){
        big:=BigData{}
        global=append(global,&big) // 全局持有引用
    }()
}

sync.WaitGroup 使用错误#

Add 在 G 内部调用、Done 漏写,Wait 永久阻塞 Add 写到 goroutine 内部:



// 错
var wg sync.WaitGroup
go func(){
    wg.Add(1) // goroutine启动后才+1
    defer wg.Done()
}()
wg.Wait()

漏写 Done: 协程提前 return 没执行Done,计数器永远不归零,Wait 永久阻塞,goroutine 常驻泄漏。

规避方案#

chan:加超时 / 关闭通道#

循环:设置退出标识、上下文 ctx.Done ()#

不用全局常驻容器存临时任务#

wg.Add 放到 goroutine 启动前,成对 Done#