golang記憶體洩漏原因有哪些
洩漏原因有:1、time.After()的使用,每次time.After(duration x)會產生NewTimer(),在duration x到期前,新建立的timer不會被GC ,到期後才會GC;2、time.NewTicker資源未及時釋放;3、select阻塞;4、channel阻塞;5、申請過多的goroutine、goroutine阻塞;6、slice引起的等。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
golang容易導致記憶體洩漏的幾種情況
#1. 定時器使用不當
1.1 time.After()的使用
預設的time.After()是會有記憶體洩漏問題的,因為每次time.After(duration x)會產生NewTimer( ),在duration x到期之前,新創建的timer不會被GC,到期之後才會GC。
隨著時間推移,尤其是duration x很大的話,會產生記憶體外洩的問題,應特別注意
for true { select { case <-time.After(time.Minute * 3): // do something default: time.Sleep(time.Duration(1) * time.Second) } }
為了保險起見,使用NewTimer()或NewTicker()代替的方式主動釋放資源,兩者的差異請自行查閱或看我往期文章https://blog.csdn.net/weixin_38299404/article/details/119352884
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) } }
#1.2 time.NewTicker資源未及時釋放
在使用time.NewTicker時需要手動呼叫Stop()方法釋放資源,否則將會造成永久性的記憶體洩漏
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阻塞
使用select時如果有case沒有覆蓋完全的情況且沒有default分支進行處理,最終會導致記憶體洩漏
2.1 導致goroutine阻塞的情況
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) } }
上述這種情況會阻塞在ch3的消費處導致記憶體洩漏
2.2 迴圈空轉導致CPU暴漲
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) }
上述for迴圈條件一旦命中default則會出現循環空轉的情況,並最終導致CPU暴漲
#3. channel阻塞
channel阻塞主要分為寫入阻塞和讀取阻塞兩種情況
空channel
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) }
寫阻塞
- 無緩衝channel的阻塞通常是寫操作因為沒有讀取而阻塞
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) }
- 有緩衝的channel因為緩衝區滿了,寫入操作阻塞
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) }
讀取阻塞
- 期待從channel讀數據,結果沒有goroutine往進寫資料
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. goroutine導致的記憶體洩漏
4.1 申請過多的goroutine
#例如在for循環中申請過多的goroutine來不及釋放導致記憶體洩漏
#4.2 goroutine阻塞
4.2. 1 I/O問題
I/O連線未設定逾時時間,導致goroutine一直在等待,程式碼會一直阻塞。
4.2.2 互斥鎖未釋放
goroutine無法取得到鎖定資源,導致goroutine阻塞
//协程拿到锁未释放,其他协程获取锁会阻塞 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.3 死鎖
當程式死鎖時其他goroutine也會阻塞
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) }
4.2. 4 waitgroup使用不當
waitgroup的Add、Done和wait數量不匹配會導致wait一直在等待
##5. slice 引起的內存洩漏
當兩個slice 共享位址,其中一個為全域變量,另一個也無法被GC;append slice 後一直使用,沒有進行清理。var a []int func test(b []int) { a = b[:3] return }
6. 陣列的值傳遞
#由於陣列時Golang的基本資料類型,每個陣列佔用不通的記憶體空間,生命週期互不干擾,很難出現記憶體洩漏的情況,但是數組作為形參傳輸時,遵循的時值拷貝,如果函數被多個goroutine調用且數組過大時,則會導致記憶體使用激增。//统计nums中target出现的次数 func countTarget(nums [1000000]int, target int) int { num := 0 for i := 0; i < len(nums) && nums[i] == target; i++ { num++ } return num }
以上是golang記憶體洩漏原因有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Go爬蟲Colly中的Queue線程問題探討在使用Go語言的Colly爬蟲庫時,開發者常常會遇到關於線程和請求隊列的問題。 �...

Go語言中用於浮點數運算的庫介紹在Go語言(也稱為Golang)中,進行浮點數的加減乘除運算時,如何確保精度是�...

Go語言中字符串打印的區別:使用Println與string()函數的效果差異在Go...

Go語言中使用RedisStream實現消息隊列時類型轉換問題在使用Go語言與Redis...

GoLand中自定義結構體標籤不顯示怎麼辦?在使用GoLand進行Go語言開發時,很多開發者會遇到自定義結構體標籤在�...

Go語言中哪些庫是大公司開發或知名開源項目?在使用Go語言進行編程時,開發者常常會遇到一些常見的需求,�...

Go語言在構建高效且可擴展的系統中表現出色,其優勢包括:1.高性能:編譯成機器碼,運行速度快;2.並發編程:通過goroutines和channels簡化多任務處理;3.簡潔性:語法簡潔,降低學習和維護成本;4.跨平台:支持跨平台編譯,方便部署。

Go編程中的資源管理:Mysql和Redis的連接與釋放在學習Go編程過程中,如何正確管理資源,特別是與數據庫和緩存�...
