站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

golang內(nèi)存泄漏原因有哪些

泄漏原因有:1、time.After()的使用,每次time.After(duration x)會產(chǎn)生NewTimer(),在duration x到期之前,新創(chuàng)建的timer不會被GC,到期之后才會GC;2、time.NewTicker資源未及時釋放;3、select阻塞;4、channel阻塞;5、申請過多的goroutine、goroutine阻塞;6、slice引起的等。

golang內(nèi)存泄漏原因有哪些

本教程操作環(huán)境:windows7系統(tǒng)、GO 1.18版本、Dell G3電腦。

golang容易導(dǎo)致內(nèi)存泄漏的幾種情況

1. 定時器使用不當(dāng)

1.1 time.After()的使用

默認(rèn)的time.After()是會有內(nèi)存泄露問題的,因?yàn)槊看蝨ime.After(duration x)會產(chǎn)生NewTimer(),在duration x到期之前,新創(chuàng)建的timer不會被GC,到期之后才會GC。

隨著時間推移,尤其是duration x很大的話,會產(chǎn)生內(nèi)存泄露的問題,應(yīng)特別注意

for true { 	select { 	case <-time.After(time.Minute * 3):     // do something   default: 		time.Sleep(time.Duration(1) * time.Second) 	} }
登錄后復(fù)制

為了保險起見,使用NewTimer()或者NewTicker()代替的方式主動釋放資源,兩者的區(qū)別請自行查閱或看我往期文章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) 	} }
登錄后復(fù)制

1.2 time.NewTicker資源未及時釋放

在使用time.NewTicker時需要手動調(diào)用Stop()方法釋放資源,否則將會造成永久性的內(nèi)存泄漏

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) 	} }
登錄后復(fù)制

2. select阻塞

使用select時如果有case沒有覆蓋完全的情況且沒有default分支進(jìn)行處理,最終會導(dǎo)致內(nèi)存泄漏

2.1 導(dǎo)致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)     } }
登錄后復(fù)制

上述這種情況會阻塞在ch3的消費(fèi)處導(dǎo)致內(nèi)存泄漏

2.2 循環(huán)空轉(zhuǎn)導(dǎo)致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) }
登錄后復(fù)制

上述for循環(huán)條件一旦命中default則會出現(xiàn)循環(huán)空轉(zhuǎn)的情況,并最終導(dǎo)致CPU暴漲

3. channel阻塞

channel阻塞主要分為寫阻塞和讀阻塞兩種情況

空channel

func channelTest() {   	//聲明未初始化的channel讀寫都會阻塞     var c chan int   	//向channel中寫數(shù)據(jù)     go func() {         c <- 1         fmt.Println("g1 send succeed")         time.Sleep(1 * time.Second)     }()   	//從channel中讀數(shù)據(jù)     go func() {         <-c         fmt.Println("g2 receive succeed")         time.Sleep(1 * time.Second)     }()     time.Sleep(10 * time.Second) }
登錄后復(fù)制

寫阻塞

  • 無緩沖channel的阻塞通常是寫操作因?yàn)闆]有讀而阻塞

func channelTest() {     var c = make(chan int)   	//10個協(xié)程向channel中寫數(shù)據(jù)     for i := 0; i < 10; i++ {         go func() {             <- c             fmt.Println("g1 receive succeed")             time.Sleep(1 * time.Second)         }()     }   	//1個協(xié)程叢channel讀數(shù)據(jù)     go func() {         c <- 1         fmt.Println("g2 send succeed")         time.Sleep(1 * time.Second)     }()   	//會有寫的9個協(xié)程阻塞得不到釋放     time.Sleep(10 * time.Second) }
登錄后復(fù)制

  • 有緩沖的channel因?yàn)榫彌_區(qū)滿了,寫操作阻塞

func channelTest() {     var c = make(chan int, 8)   	//10個協(xié)程向channel中寫數(shù)據(jù)     for i := 0; i < 10; i++ {         go func() {             <- c             fmt.Println("g1 receive succeed")             time.Sleep(1 * time.Second)         }()     }   	//1個協(xié)程叢channel讀數(shù)據(jù)     go func() {         c <- 1         fmt.Println("g2 send succeed")         time.Sleep(1 * time.Second)     }()   	//會有寫的幾個協(xié)程阻塞寫不進(jìn)去     time.Sleep(10 * time.Second) }
登錄后復(fù)制

讀阻塞

  • 期待從channel讀數(shù)據(jù),結(jié)果沒有g(shù)oroutine往進(jìn)寫數(shù)據(jù)

func channelTest() {    var c = make(chan int)   //1個協(xié)程向channel中寫數(shù)據(jù)   go func() {     <- c     fmt.Println("g1 receive succeed")     time.Sleep(1 * time.Second)   }()   //10個協(xié)程叢channel讀數(shù)據(jù)   for i := 0; i < 10; i++ {     go func() {         c <- 1         fmt.Println("g2 send succeed")         time.Sleep(1 * time.Second)     }()   }   //會有讀的9個協(xié)程阻塞得不到釋放   time.Sleep(10 * time.Second) }
登錄后復(fù)制

4. goroutine導(dǎo)致的內(nèi)存泄漏

4.1 申請過多的goroutine

例如在for循環(huán)中申請過多的goroutine來不及釋放導(dǎo)致內(nèi)存泄漏

4.2 goroutine阻塞

4.2.1 I/O問題

I/O連接未設(shè)置超時時間,導(dǎo)致goroutine一直在等待,代碼會一直阻塞。

4.2.2 互斥鎖未釋放

goroutine無法獲取到鎖資源,導(dǎo)致goroutine阻塞

//協(xié)程拿到鎖未釋放,其他協(xié)程獲取鎖會阻塞 func mutexTest() {     mutex := sync.Mutex{}     for i := 0; i < 10; i++ {         go func() {             mutex.Lock()             fmt.Printf("%d goroutine get mutex", i)       			//模擬實(shí)際開發(fā)中的操作耗時             time.Sleep(100 * time.Millisecond)         }()     }     time.Sleep(10 * time.Second) }
登錄后復(fù)制

4.2.3 死鎖

當(dāng)程序死鎖時其他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")     }()   	//其余協(xié)程獲取鎖都會失敗     go func() {         m1.Lock()         fmt.Println("g3 get m1")     }()     time.Sleep(10 * time.Second) }
登錄后復(fù)制

4.2.4 waitgroup使用不當(dāng)

waitgroup的Add、Done和wait數(shù)量不匹配會導(dǎo)致wait一直在等待

5. slice 引起的內(nèi)存泄漏

當(dāng)兩個slice 共享地址,其中一個為全局變量,另一個也無法被GC;

append slice 后一直使用,沒有進(jìn)行清理。

var a []int   func test(b []int) {         a = b[:3]         return }
登錄后復(fù)制

6. 數(shù)組的值傳遞

由于數(shù)組時Golang的基本數(shù)據(jù)類型,每個數(shù)組占用不通的內(nèi)存空間,生命周期互不干擾,很難出現(xiàn)內(nèi)存泄漏的情況,但是數(shù)組作為形參傳輸時,遵循的時值拷貝,如果函數(shù)被多個goroutine調(diào)用且數(shù)組過大時,則會導(dǎo)致內(nèi)存使用激增。

//統(tǒng)計(jì)nums中target出現(xiàn)的次數(shù) func countTarget(nums [1000000]int, target int) int {     num := 0     for i := 0; i < len(nums) && nums[i] == target; i++ {         num++     }     return num }
登錄后復(fù)制

因此對于大數(shù)組放在形參場景下通常使用切片或者指針進(jìn)行傳遞,避免短時間的內(nèi)存使用激增、

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
国产日韩精品SUV| 日韩一区二区超清视频| 久久国产精品范冰啊| 久久精品国产精品亚洲| 国产亚洲精品影视在线| 久久久久久国产精品mv| 中文字幕久久精品无码| 精品人妻无码专区中文字幕 | 精品视频国产狼友视频| 99精品国产高清一区二区麻豆| 久久久久国产精品熟女影院| 久久青草精品38国产免费| 国产精品一久久香蕉国产线看观看 | 国产精品永久久久久久久久久| 精品一区二区三区四区电影 | 日韩精品无码视频一区二区蜜桃 | 国内精品免费视频自在线 | 国产精品麻豆欧美日韩WW| 国产一区二区精品在线观看| 国产精品盗摄一区二区在线| 精品久久久久不卡无毒| 网曝门精品国产事件在线观看 | av无码精品一区二区三区四区| 欧美日韩久久久精品A片| 日韩人妻不卡一区二区三区| 亚洲?V无码成人精品区日韩| 亚洲精品无码日韩国产不卡?V| 国产四虎精品8848hh| 国产成人精品男人的天堂网站| 国产在线精品无码二区二区| 国产精品9999久久久久仙踪林| 国产亚洲精品美女2020久久| 日韩在线观看免费| 欧洲MV日韩MV国产| 日韩不卡视频在线观看| 男女男精品网站免费观看| 亚洲精品成人区在线观看| 国产精品成人小电影在线观看 | 91精品国产综合久| 亚洲国产精品乱码在线观看97| 91嫩草亚洲精品|