Home>Article>Backend Development> What are the causes of golang memory leaks?

What are the causes of golang memory leaks?

青灯夜游
青灯夜游 Original
2023-01-10 17:45:48 2231browse

The reasons for the leak are: 1. The use of time.After(). Each time.After(duration x) will generate NewTimer(). Before duration x expires, the newly created timer will not be GC , GC will only occur after expiration; 2. time.NewTicker resources are not released in time; 3. select blocking; 4. channel blocking; 5. applying for too many goroutines, goroutine blocking; 6. caused by slice, etc.

What are the causes of golang memory leaks?

The operating environment of this tutorial: Windows 7 system, GO version 1.18, Dell G3 computer.

Several situations in which golang can easily lead to memory leaks

1. Improper use of timers

1.1 The use of time.After()

The default time.After() will have a memory leak problem, because each time.After(duration x) will generate NewTimer( ), the newly created timer will not be GCed before duration x expires, and will only be GCed after expiration.

As time goes by, especially if the duration The method actively releases resources. Please check the difference between the two by yourself or read my previous articles https://blog.csdn.net/weixin_38299404/article/details/119352884

for true { select { case <-time.After(time.Minute * 3): // do something default: time.Sleep(time.Duration(1) * time.Second) } }

1.2 time.NewTicker resources are not released in timeWhen using time.NewTicker, you need to manually call the Stop() method to release resources, otherwise it will cause a permanent memory leak

timer := time.NewTicker(time.Duration(2) * time.Second) defer timer.Stop() for true { select { case <-timer.C: // do something default: time.Sleep(time.Duration(1) * time.Second) } }

2. Select blockingWhen using select, if there is a case that does not fully cover the situation and there is no default branch for processing, it will eventually lead to memory leaks

2.1 Situations that cause goroutine to block
timer := time.NewTicker(time.Duration(2) * time.Second) // defer timer.Stop() for true { select { case <-timer.C: // do something default: time.Sleep(time.Duration(1) * time.Second) } }
The above situation will block the consumption of ch3 and cause memory leaks

2.2 Loop idling causes CPU surge
func main() { ch1 := make(chan int) ch2 := make(chan int) ch3 := make(chan int) go Getdata("https://www.baidu.com",ch1) go Getdata("https://www.baidu.com",ch2) go Getdata("https://www.baidu.com",ch3) select{ case v:=<- ch1: fmt.Println(v) case v:=<- ch2: fmt.Println(v) } }
Once the above for loop condition hits default, loop idling will occur, which will eventually lead to CPU surge

3. Channel blockingChannel blocking is mainly divided into two situations: write blocking and read blocking

Empty channel

func main() { fmt.Println("main start") msgList := make(chan int, 100) go func() { for { select { case <-msgList: default: } } }() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) s := <-c fmt.Println("main exit.get signal:", s) }

Write blocking

The blocking of unbuffered channel is usually that the write operation blocks because there is no read.
  • func channelTest() { //声明未初始化的channel读写都会阻塞 var c chan int //向channel中写数据 go func() { c <- 1 fmt.Println("g1 send succeed") time.Sleep(1 * time.Second) }() //从channel中读数据 go func() { <-c fmt.Println("g2 receive succeed") time.Sleep(1 * time.Second) }() time.Sleep(10 * time.Second) }

The buffered channel is blocked because the buffer is full. , write operation blocked
  • func channelTest() { var c = make(chan int) //10个协程向channel中写数据 for i := 0; i < 10; i++ { go func() { <- c fmt.Println("g1 receive succeed") time.Sleep(1 * time.Second) }() } //1个协程丛channel读数据 go func() { c <- 1 fmt.Println("g2 send succeed") time.Sleep(1 * time.Second) }() //会有写的9个协程阻塞得不到释放 time.Sleep(10 * time.Second) }
  • read blocked

Waiting to read data from the channel, but no goroutine went in to write data
  • func channelTest() { var c = make(chan int, 8) //10个协程向channel中写数据 for i := 0; i < 10; i++ { go func() { <- c fmt.Println("g1 receive succeed") time.Sleep(1 * time.Second) }() } //1个协程丛channel读数据 go func() { c <- 1 fmt.Println("g2 send succeed") time.Sleep(1 * time.Second) }() //会有写的几个协程阻塞写不进去 time.Sleep(10 * time.Second) }

4. Memory leak caused by goroutine

4.1 Apply for too many goroutines

For example, applying for too many goroutines in a for loop and not releasing them in time leads to memory leaks

##4.2 Goroutine blocking4.2. 1 I/O problem

The I/O connection does not set a timeout, causing the goroutine to keep waiting and the code to keep blocking.

4.2.2 The mutex lock is not releasedgoroutine cannot obtain the lock resource, causing goroutine to block
func channelTest() { var c = make(chan int) //1个协程向channel中写数据 go func() { <- c fmt.Println("g1 receive succeed") time.Sleep(1 * time.Second) }() //10个协程丛channel读数据 for i := 0; i < 10; i++ { go func() { c <- 1 fmt.Println("g2 send succeed") time.Sleep(1 * time.Second) }() } //会有读的9个协程阻塞得不到释放 time.Sleep(10 * time.Second) }

4.2.3 DeadlockWhen the program deadlocks, other goroutines will also block
//协程拿到锁未释放,其他协程获取锁会阻塞 func mutexTest() { mutex := sync.Mutex{} for i := 0; i < 10; i++ { go func() { mutex.Lock() fmt.Printf("%d goroutine get mutex", i) //模拟实际开发中的操作耗时 time.Sleep(100 * time.Millisecond) }() } time.Sleep(10 * time.Second) }

4.2. 4 Improper use of waitgroupThe mismatch in the number of Add, Done and wait of waitgroup will cause wait to keep waiting

5. Memory caused by slice LeakWhen two slices share an address, one of which is a global variable, the other cannot be GC;

It has been used after appending the slice without cleaning it.

func mutexTest() { m1, m2 := sync.Mutex{}, sync.RWMutex{} //g1得到锁1去获取锁2 go func() { m1.Lock() fmt.Println("g1 get m1") time.Sleep(1 * time.Second) m2.Lock() fmt.Println("g1 get m2") }() //g2得到锁2去获取锁1 go func() { m2.Lock() fmt.Println("g2 get m2") time.Sleep(1 * time.Second) m1.Lock() fmt.Println("g2 get m1") }() //其余协程获取锁都会失败 go func() { m1.Lock() fmt.Println("g3 get m1") }() time.Sleep(10 * time.Second) }

#6. Array value transferSince arrays are the basic data type of Golang, each array occupies a different amount of memory space. The cycles do not interfere with each other, so it is difficult to cause memory leaks. However, when the array is transmitted as a formal parameter, the time value copy is followed. If the function is called by multiple goroutines and the array is too large, it will cause a surge in memory usage.
var a []int func test(b []int) { a = b[:3] return }

Therefore, when large arrays are placed in formal parameter scenarios, slices or pointers are usually used to pass them to avoid a short-term surge in memory usage.

[Related recommendations:

Go video tutorial

Programming teaching

The above is the detailed content of What are the causes of golang memory leaks?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn