0%

golang内存和goroutine泄露的场景

1. 内存泄露

  1. 假设 a 是一个大slice,而 b 只引用了一小部分,这就造成了底层数组其他未被引用的部分内存泄漏。

    1
    2
    3
    4
    5
    func sliceLeak() {
    data := make([]byte, 1000000)
    slice := data[:10] // 这个切片依然引用整个数组
    _ = slice
    }
  2. 因为协程被永久阻塞而造成的永久性内存泄露。

  3. 未正确释放文件、数据库连接等资源,导致内存无法释放。

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

  5. 全局变量或长生命周期的对象持有大量数据,无法被垃圾回收。

  6. 在一些复杂的数据结构中,可能会出现循环引用,导致垃圾回收器无法回收。

  7. 使用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)
}
// do something...
}()

time.Sleep(time.Second * 1)
fmt.Println("goroutines: ", runtime.NumGoroutine())
}
}

3. 参考资料

可以加首页作者微信,咨询相关问题!