1. 内存泄露
假设 a
是一个大slice,而 b
只引用了一小部分,这就造成了底层数组其他未被引用的部分内存泄漏。
1 2 3 4 5
| func sliceLeak() { data := make([]byte, 1000000) slice := data[:10] _ = slice }
|
因为协程被永久阻塞而造成的永久性内存泄露。
未正确释放文件、数据库连接等资源,导致内存无法释放。
延迟调用函数导致的临时性内存泄露。
全局变量或长生命周期的对象持有大量数据,无法被垃圾回收。
在一些复杂的数据结构中,可能会出现循环引用,导致垃圾回收器无法回收。
使用time.Timer或time.Ticker时,未及时调用Stop方法。
2. goroutine泄露
2.1 channel 发送不接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func main() { for i := 0; i < 4; i++ { queryAll() fmt.Printf("goroutines: %d\n", runtime.NumGoroutine()) } }
func queryAll() int { ch := make(chan int) for i := 0; i < 3; i++ { go func() { ch <- query() }() } return <-ch }
func query() int { n := rand.Intn(100) time.Sleep(time.Duration(n) * time.Millisecond) return n }
|
2.2 channel 接收不发送(没有机会让别人发送)
1 2 3 4 5 6 7 8 9
| func leak(w http.ResponseWriter, r *http.Request) { ch := make(chan bool, 0) go func() { fmt.Println("异步任务做一些操作") <-ch }()
w.Write([]byte("will leak")) }
|
2.3 nil channel
1 2 3 4 5 6 7 8 9 10 11 12
| func main() { defer func() { fmt.Println("goroutines: ", runtime.NumGoroutine()) }()
var ch chan int go func() { <-ch }() time.Sleep(time.Second) }
|
channel 如果忘记初始化,那么无论你是读,还是写操作,都会造成阻塞。
2.4 互斥锁忘记解锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func main() { total := 0 defer func() { time.Sleep(time.Second) fmt.Println("total: ", total) fmt.Println("goroutines: ", runtime.NumGoroutine()) }()
var mutex sync.Mutex for i := 0; i < 10; i++ { go func() { mutex.Lock() total += 1 }() } }
|
2.5 WaitGroup 使用不当
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| func handle(v int) { var wg sync.WaitGroup wg.Add(5) for i := 0; i < v; i++ { fmt.Println("脑子进煎鱼了") wg.Done() } wg.Wait() }
func main() { defer func() { fmt.Println("goroutines: ", runtime.NumGoroutine()) }()
go handle(3) time.Sleep(time.Second) }
|
2.6 http client 没有设置超时时间等
Go 语言中默认的 http.Client 是没有设置超时时间的。
因此就会导致一直阻塞,一直阻塞就一直爽,Goroutine 自然也就持续暴涨,不断泄露,最终占满资源,导致事故。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func main() { for { go func() { _, err := http.Get("https://www.xxx.com/") if err != nil { fmt.Printf("http.Get err: %v\n", err) } }()
time.Sleep(time.Second * 1) fmt.Println("goroutines: ", runtime.NumGoroutine()) } }
|
3. 参考资料