亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang內存泄漏的原因是什么

發布時間:2023-01-13 17:57:58 來源:億速云 閱讀:140 作者:iii 欄目:編程語言

這篇“golang內存泄漏的原因是什么”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“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引起的等。

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()代替的方式主動釋放資源

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() {
    ch2 := make(chan int)
    ch3 := make(chan int)
    ch4 := make(chan int)
    go Getdata("https://www.baidu.com",ch2)
    go Getdata("https://www.baidu.com",ch3)
    go Getdata("https://www.baidu.com",ch4)
    select{
        case v:=<- ch2:
            fmt.Println(v)
        case v:=<- ch3:
            fmt.Println(v)
    }
}

上述這種情況會阻塞在ch4的消費處導致內存泄漏

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內存泄漏的原因是什么”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

安图县| 盖州市| 宜春市| 军事| 泰和县| 惠来县| 荆州市| 铜梁县| 牡丹江市| 阿克陶县| 靖边县| 方城县| 金塔县| 洛隆县| 苏尼特左旗| 台北县| 常山县| 台北市| 洪洞县| 横峰县| 耒阳市| 南华县| 泰兴市| 台前县| 清镇市| 东方市| 伊金霍洛旗| 铅山县| 泽州县| 阿克陶县| 宾川县| 花垣县| 施秉县| 新蔡县| 谷城县| 象山县| 晋州市| 长宁县| 卢湾区| 姚安县| 金寨县|