最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang因Channel未關閉導致內(nèi)存泄漏的解決方案詳解

 更新時間:2023年07月24日 09:27:31   作者:Paualf  
這篇文章主要為大家詳細介紹了當Golang因Channel未關閉導致內(nèi)存泄漏時蓋如何解決,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下

現(xiàn)象

某一個周末我們的服務 oom了,一個比較重要的job 沒有跑完,需要重跑,以為是偶然,重跑成功,因為是周末沒有去定位原因
又一個工作日,它又oom了,重跑成功,持續(xù)觀察,job 在oom之前竟然占用了30g左右(這里我們的任務比較大的數(shù)據(jù)量都在內(nèi)存中計算,所以這里機器內(nèi)存量大一點)

應用使用30g內(nèi)存肯定是不正常的,懷疑內(nèi)存泄漏了,怎么定位內(nèi)存泄漏呢?

定位

搜了一下網(wǎng)上經(jīng)常用到的工具是 go 的 pprof 火焰圖,自己在本地跑了一下,因為數(shù)據(jù)量比較少,并沒有發(fā)現(xiàn)什么,暫時放下了。
后續(xù)某個早上在公司工具里面打開了一下,發(fā)現(xiàn)有火焰圖的工具,打開看了一下一個函數(shù)占用了 7224.46mb,占用了 7個g, 而且這個函數(shù)是已經(jīng)跑完了,這個時候定位到那個函數(shù)了,和旁邊同事說了一下,同事幫忙看了下郵件告警,每個下午都會有任務失敗告警(任務失敗會進行重試的); 這里懷疑是失敗了, channel 沒有關閉,導致 消費的go routine 沒有回收。

舉個例子看下代碼:

package main
import (
	"context"
	"fmt"
	"golang.org/x/sync/errgroup"
)
func main() {
	readGroup, _ := errgroup.WithContext(context.Background())
	consumeGroup, _ := errgroup.WithContext(context.Background())
	var (
		data = make(chan []int, 10)
	)
	//  3個生產(chǎn)者往里面進行進行生產(chǎn)
	readGroup.Go(func() error {
		for i := 0; i < 3; i++ {
			data <- []int{i}
		}
		return nil
	})
	readGroup.Go(func() error {
		for i := 3; i < 6; i++ {
			data <- []int{i}
		}
		return nil
	})
	readGroup.Go(func() (err error) {
		for i := 6; i < 9; i++ {
			// error
			if i == 7 {
				err = fmt.Errorf("error le")
				return
			}
			data <- []int{i}
		}
		return nil
	})
	// 其中一個生產(chǎn)者遇到error 返回導致 channel 沒有關閉,消費者沒有退出
	// 1個消費者進行消費
	consumeGroup.Go(func() error {
		for i := range data {
			fmt.Println(i)
		}
		return nil
	})
	if err := readGroup.Wait(); err != nil {
		fmt.Println(err)
		return
	}
	close(data)
	if err := consumeGroup.Wait(); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("end it")
}

這個case里面,readGroup 遇到error 直接退出了,channel并沒有關閉,如果是常駐進程的程序,消費的go routine 并沒有回收,就導致了內(nèi)存泄漏

最簡單的關閉修復

將 close 放到最上面的 defer close(data)

不過最好的還是生產(chǎn)者進行關閉,我們可以優(yōu)化一下代碼,把生產(chǎn)者的代碼放到一個函數(shù)中,這樣就可以讓生產(chǎn)者去進行關閉的操作了

package main
import (
	"context"
	"fmt"
	"golang.org/x/sync/errgroup"
)
func main() {
	var (
		data = make(chan []int, 10)
		err  error
		eg, _ = errgroup.WithContext(context.Background())
	)
	eg.Go(func() (err error) {
		defer close(data)
		err = readGroup(data)
		return
	})
	eg.Go(func() (err error) {
		err = consumeGroup(data)
		return
	})
	err = eg.Wait()
	if err != nil {
		return
	}
	fmt.Println("end it")
}
func consumeGroup(data chan []int) (err error) {
	consumeGroup, _ := errgroup.WithContext(context.Background())
	consumeGroup.Go(func() error {
		for i := range data {
			fmt.Println(i)
		}
		return nil
	})
	if err = consumeGroup.Wait(); err != nil {
		fmt.Println(err)
		return
	}
	return
}
func readGroup(data chan []int) (err error) {
	readGroup, _ := errgroup.WithContext(context.Background())
	//  3個生產(chǎn)者往里面進行進行生產(chǎn)
	readGroup.Go(func() error {
		for i := 0; i < 3; i++ {
			data <- []int{i}
		}
		return nil
	})
	readGroup.Go(func() error {
		for i := 3; i < 6; i++ {
			data <- []int{i}
		}
		return nil
	})
	readGroup.Go(func() (err error) {
		for i := 6; i < 9; i++ {
			// error
			if i == 7 {
				err = fmt.Errorf("error le")
				return
			}
			data <- []int{i}
		}
		return nil
	})
	if err = readGroup.Wait(); err != nil {
		fmt.Println(err)
		return
	}
	return
}

修復

將生產(chǎn)者放在一個 go routine 里面,最后如果遇到error的話 defer()的時候會把channel給關閉了

The Channel Closing Principle
One general principle of using Go channels is don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders. In other words, we should only close a channel in a sender goroutine if the sender is the only sender of the channel.

簡單點:就是在生產(chǎn)者中進行channel的關閉

后續(xù)討論和遇到的新問題

拆分代碼函數(shù)的時候又遇到新的問題了,有一個切片數(shù)組我拆分函數(shù)的時候,我沒有去接受切片函數(shù)的返回值,導致了切片發(fā)生擴容返回的是一個空切片,并沒有修改掉原來的切片。之前以為在golang里面切片是引用類型,會自動改變其中的值最后查了一下,在go 里面都是值傳遞,可以修改其中的值其實是使用了指針修改了同一塊地址中的值所以值發(fā)生了變化

總結(jié)

使用channel 的時候在生產(chǎn)者中進行關閉,思考一些遇到error的時候channel是否可以正常的關閉

go 中只有值傳遞,引用傳遞是修改了同一個指向內(nèi)存地址中的值

參考文章

Golang優(yōu)雅關閉channel的方法示例

Go語言參數(shù)傳遞是傳值還是傳引用

到此這篇關于Golang因Channel未關閉導致內(nèi)存泄漏的解決方案詳解的文章就介紹到這了,更多相關Golang Channel內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • golang?beego框架環(huán)境搭建過程

    golang?beego框架環(huán)境搭建過程

    這篇文章主要為大家介紹了golang?beego框架環(huán)境搭建的過程腳本,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • 基于Golang實現(xiàn)內(nèi)存數(shù)據(jù)庫的示例詳解

    基于Golang實現(xiàn)內(nèi)存數(shù)據(jù)庫的示例詳解

    這篇文章主要為大家詳細介紹了如何基于Golang實現(xiàn)內(nèi)存數(shù)據(jù)庫,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的小伙伴可以參考一下
    2023-03-03
  • golang?四則運算計算器yacc歸約手寫實現(xiàn)

    golang?四則運算計算器yacc歸約手寫實現(xiàn)

    這篇文章主要為大家介紹了golang?四則運算?計算器?yacc?歸約的手寫實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Go語言實戰(zhàn)學習之流程控制詳解

    Go語言實戰(zhàn)學習之流程控制詳解

    這篇文章主要為大家詳細介紹了Go語言中的流程控制,文中的示例代碼講解詳細,對我們學習Go語言有一定的幫助?,需要的朋友可以參考下
    2022-08-08
  • Go語言字符串及strings和strconv包使用實例

    Go語言字符串及strings和strconv包使用實例

    字符串是工作中最常用的,值得我們專門的練習一下,下面這篇文章主要給大家介紹了關于Go語言字符串及strings和strconv包使用的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-06-06
  • Golang使用gob實現(xiàn)結(jié)構(gòu)體的序列化過程詳解

    Golang使用gob實現(xiàn)結(jié)構(gòu)體的序列化過程詳解

    Golang struct類型數(shù)據(jù)序列化用于網(wǎng)絡傳輸數(shù)據(jù)或在磁盤上寫入數(shù)據(jù)。在分布式系統(tǒng)中,一端生成數(shù)據(jù)、然后序列化、壓縮和發(fā)送;在另一端,接收數(shù)據(jù)、然后解壓縮、反序列化和處理數(shù)據(jù),整個過程必須快速有效
    2023-03-03
  • Go實現(xiàn)socks5服務器的方法

    Go實現(xiàn)socks5服務器的方法

    SOCKS5 是一個代理協(xié)議,它在使用TCP/IP協(xié)議通訊的前端機器和服務器機器之間扮演一個中介角色,使得內(nèi)部網(wǎng)中的前端機器變得能夠訪問Internet網(wǎng)中的服務器,或者使通訊更加安全,這篇文章主要介紹了Go實現(xiàn)socks5服務器的方法,需要的朋友可以參考下
    2023-07-07
  • 詳解go 中的 fmt 占位符

    詳解go 中的 fmt 占位符

    這篇文章主要介紹了go 中的 fmt 占位符,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • Golang中的閉包(Closures)詳解

    Golang中的閉包(Closures)詳解

    在?Golang?中,閉包是一個引用了作用域之外的變量的函數(shù),Golang?中的匿名函數(shù)也被稱為閉包,閉包可以被認為是一種特殊類型的匿名函數(shù),所以本文就給大家詳細的介紹一下Golang的閉包到底是什么,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07
  • GoFrame框架gset交差并補集使用實例

    GoFrame框架gset交差并補集使用實例

    這篇文章主要為大家介紹了GoFrame框架gset交差并補集使用實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06

最新評論

平舆县| 京山县| 当涂县| 姚安县| 巫山县| 广灵县| 淮北市| 怀来县| 东丽区| 岫岩| 临安市| 定南县| 万全县| 贵港市| 贵阳市| 鱼台县| 昭觉县| 通州市| 巴里| 永安市| 呼图壁县| 青阳县| 托克托县| 抚远县| 阳泉市| 东阿县| 高碑店市| 赤峰市| 轮台县| 奈曼旗| 杂多县| 武川县| 山阳县| 乌苏市| 凌云县| 通化市| 青神县| 信宜市| 恩施市| 德阳市| 永州市|