欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go語言內(nèi)存泄漏的常見形式

 更新時間:2025年04月14日 08:33:33   作者:Achilles.Wang  
本文主要介紹了go語言內(nèi)存泄漏的常見形式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

go語言內(nèi)存泄漏

在這里插入圖片描述

子字符串導(dǎo)致的內(nèi)存泄漏

使用自動垃圾回收的語言進(jìn)行編程時,通常我們無需擔(dān)心內(nèi)存泄漏的問題,因為運(yùn)行時會定期回收未使用的內(nèi)存。但是如果你以為這樣就完事大吉了,哪里就大錯特措了。

因為,雖然go中并未對字符串時候共享底層內(nèi)存塊進(jìn)行規(guī)定,但go語言編譯器/運(yùn)行時默認(rèn)情況下允許字符串共享底層內(nèi)存塊,直到原先的字符串指向的內(nèi)存被修改才會進(jìn)行寫時復(fù)制,這是一個很好的設(shè)計,既能節(jié)省內(nèi)存,又能節(jié)省CPU資源,但有時也會導(dǎo)致"內(nèi)存泄漏"。

例如如下代碼,一旦調(diào)用demo就會導(dǎo)致將近1M內(nèi)存的泄漏,因為s0只使用了50字節(jié),但是會導(dǎo)致1M的內(nèi)存一直無法被回收,這些內(nèi)存會一直持續(xù)到下次s0被修改的時候才會被釋放掉。

var s0 string // a package-level variable

// A demo purpose function.
func f(s1 string) {
	s0 = s1[:50]
	// Now, s0 shares the same underlying memory block
	// with s1. Although s1 is not alive now, but s0
	// is still alive, so the memory block they share
	// couldn't be collected, though there are only 50
	// bytes used in the block and all other bytes in
	// the block become unavailable.
}

func demo() {
	s := createStringWithLengthOnHeap(1 << 20) // 1M bytes
	f(s)
}

為了避免這種內(nèi)存泄漏,我們可以使用[]byte來替代原先的1M大小的內(nèi)存,不過這樣會有兩次50字節(jié)的內(nèi)存重復(fù)

func f(s1 string) {
	s0 = string([]byte(s1[:50]))
}

當(dāng)然我們也可以利用go編譯器的優(yōu)化來避免不必要的重復(fù),只需要浪費一個字節(jié)內(nèi)存就行

func f(s1 string) {
	s0 = (" " + s1[:50])[1:]
}

上述方法的缺點是編譯器優(yōu)化以后可能會失效,并且其他編譯器可能無法提供該優(yōu)化

避免此類內(nèi)存泄漏的第三種方法是利用 Go 1.10 以來支持的 strings.Builder 。

import "strings"

func f(s1 string) {
	var b strings.Builder
	b.Grow(50)
	b.WriteString(s1[:50])
	s0 = b.String()
}

從 Go 1.18 開始, strings 標(biāo)準(zhǔn)庫包中新增了 Clone 函數(shù),這成為了完成這項工作的最佳方式。

子切片導(dǎo)致的內(nèi)存泄漏

同樣場景下,切片也會導(dǎo)致內(nèi)存的浪費

與子字符串類似,子切片也可能導(dǎo)致某種內(nèi)存泄漏。在下面的代碼中,調(diào)用 g 函數(shù)后,保存 s1 元素的內(nèi)存塊所占用的大部分內(nèi)存將會丟失(如果沒有其他值引用該內(nèi)存塊)。

var s0 []int

func g(s1 []int) {
	// Assume the length of s1 is much larger than 30.
	s0 = s1[len(s1)-30:]
}

如果我們想避免這種內(nèi)存泄漏,我們必須復(fù)制 s0 的 30 個元素,這樣 s0 的活躍性就不會阻止收集承載 s1 元素的內(nèi)存塊。

func g(s1 []int) {
	s0 = make([]int, 30)
	copy(s0, s1[len(s1)-30:])
	// Now, the memory block hosting the elements
	// of s1 can be collected if no other values
	// are referencing the memory block.
}

未重置子切片指針導(dǎo)致的內(nèi)存泄漏

在下面的代碼中,調(diào)用 h 函數(shù)后,為切片 s 的第一個和最后一個元素分配的內(nèi)存塊將丟失。

func h() []*int {
	s := []*int{new(int), new(int), new(int), new(int)}
	// do something with s ...
	// 返回一個從1開始,不能到索引3的新切片, 也就是 s[1], s[2]
	return s[1:3:3]
}

只要返回的切片仍然有效,它就會阻止收集 s 的任何元素,從而阻止收集為 s 的第一個和最后一個元素引用的兩個 int 值分配的兩個內(nèi)存塊。

如果我們想避免這種內(nèi)存泄漏,我們必須重置丟失元素中存儲的指針。

func h() []*int {
	s := []*int{new(int), new(int), new(int), new(int)}
	// do something with s ...

	// Reset pointer values.
	s[0], s[len(s)-1] = nil, nil
	return s[1:3:3]
}

掛起Goroutine導(dǎo)致的內(nèi)存泄漏

有時,Go 程序中的某些 goroutine 可能會永遠(yuǎn)處于阻塞狀態(tài)。這樣的 goroutine 被稱為掛起的 goroutine。Go 運(yùn)行時不會終止掛起的 goroutine,因此為掛起的 goroutine 分配的資源(以及它們引用的內(nèi)存塊)永遠(yuǎn)不會被垃圾回收。

Go 運(yùn)行時不會殺死掛起的 Goroutine 有兩個原因。一是 Go 運(yùn)行時有時很難判斷一個阻塞的 Goroutine 是否會被永久阻塞。二是我們有時會故意讓 Goroutine 掛起。例如,有時我們可能會讓 Go 程序的主 Goroutine 掛起,以避免程序退出。

如果不停止time.Ticker也會導(dǎo)致內(nèi)存泄漏

當(dāng) time.Timer 值不再使用時,它會在一段時間后被垃圾回收。但 time.Ticker 值則不然。我們應(yīng)該在 time.Ticker 值不再使用時停止它。

不正確地使用終結(jié)器會導(dǎo)致真正的內(nèi)存泄漏

為屬于循環(huán)引用組的成員值設(shè)置終結(jié)器(finalizer)可能會阻止為該循環(huán)引用組分配的所有內(nèi)存塊被回收。這是真正的內(nèi)存泄漏,不是某種假象。

例如,在調(diào)用并退出以下函數(shù)后,分配給 x 和 y 的內(nèi)存塊不能保證在未來的垃圾收集中被收集。

func memoryLeaking() {
	type T struct {
		v [1<<20]int
		t *T
	}

	var finalizer = func(t *T) {
		 fmt.Println("finalizer called")
	}

	var x, y T

	// The SetFinalizer call makes x escape to heap.
	runtime.SetFinalizer(&x, finalizer)

	// The following line forms a cyclic reference
	// group with two members, x and y.
	// This causes x and y are not collectable.
	x.t, y.t = &y, &x // y also escapes to heap.
}

因此,請避免為循環(huán)引用組中的值設(shè)置終結(jié)器。

延遲函數(shù)調(diào)用導(dǎo)致的某種資源泄漏

非常大的延遲調(diào)用堆棧也可能會消耗大量內(nèi)存,并且如果某些調(diào)用延遲太多,某些資源可能無法及時釋放。

例如,如果在調(diào)用以下函數(shù)時需要處理許多文件,那么在函數(shù)退出之前將有大量文件處理程序無法釋放。

func writeManyFiles(files []File) error {
	for _, file := range files {
		f, err := os.Open(file.path)
		if err != nil {
			return err
		}
		defer f.Close()

		_, err = f.WriteString(file.content)
		if err != nil {
			return err
		}

		err = f.Sync()
		if err != nil {
			return err
		}
	}

	return nil
}

對于這種情況,我們可以使用匿名函數(shù)來封裝延遲調(diào)用,以便延遲函數(shù)調(diào)用能夠更早地執(zhí)行。例如,上面的函數(shù)可以重寫并改進(jìn)為

func writeManyFiles(files []File) error {
	for _, file := range files {
		if err := func() error {
			f, err := os.Open(file.path)
			if err != nil {
				return err
			}
			// The close method will be called at
			// the end of the current loop step.
			defer f.Close()

			_, err = f.WriteString(file.content)
			if err != nil {
				return err
			}

			return f.Sync()
		}(); err != nil {
			return err
		}
	}

	return nil
}

當(dāng)然不要犯以下錯誤,需要有些同學(xué)將需要延時調(diào)用的函數(shù)字節(jié)省略,導(dǎo)致資源泄漏

_, err := os.Open(file.path)

如果是http請求,還會導(dǎo)致服務(wù)端擠壓大量的連接無法釋放

到此這篇關(guān)于go語言內(nèi)存泄漏的常見形式的文章就介紹到這了,更多相關(guān)go語言內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go-RESTful實現(xiàn)下載功能思路詳解

    Go-RESTful實現(xiàn)下載功能思路詳解

    這篇文章主要介紹了Go-RESTful實現(xiàn)下載功能,文件下載包括文件系統(tǒng)IO和網(wǎng)絡(luò)IO,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-10-10
  • go語言實現(xiàn)依賴注入的示例代碼

    go語言實現(xiàn)依賴注入的示例代碼

    依賴注入和控制反轉(zhuǎn)恰恰相反,它是一種具體的編碼技巧,我們不通過 new 的方式在類內(nèi)部創(chuàng)建依賴類的對象,而是將依賴的類對象在外部創(chuàng)建好之后,通過構(gòu)造函數(shù)、函數(shù)參數(shù)等方式傳遞給類來使用,本文將給大家介紹go語言實現(xiàn)依賴注入,需要的朋友可以參考下
    2024-01-01
  • 解決goland中編輯tpl文件不高亮沒智能補(bǔ)全的問題

    解決goland中編輯tpl文件不高亮沒智能補(bǔ)全的問題

    這篇文章主要介紹了解決goland中編輯tpl文件不高亮沒智能補(bǔ)全的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言中strings.HasPrefix、strings.Split、strings.SplitN()?函數(shù)

    Go語言中strings.HasPrefix、strings.Split、strings.SplitN()?函數(shù)

    本文主要介紹了Go語言中strings.HasPrefix、strings.Split、strings.SplitN()函數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • golang優(yōu)化目錄遍歷的實現(xiàn)方法

    golang優(yōu)化目錄遍歷的實現(xiàn)方法

    對于go1.16的新變化,大家印象最深的可能是io包的大規(guī)模重構(gòu),但這個重構(gòu)實際上還引進(jìn)了一個優(yōu)化,這篇文章要說的就是這個優(yōu)化,所以本將給大家介紹golang是如何優(yōu)化目錄遍歷的,需要的朋友可以參考下
    2024-08-08
  • golang使用go test輸出單元測試覆蓋率的方式

    golang使用go test輸出單元測試覆蓋率的方式

    單元測試覆蓋率是衡量代碼質(zhì)量的一個重要指標(biāo),重要的代碼文件覆蓋率應(yīng)該至少達(dá)到80%以上,Java 可以通過JaCoCo 統(tǒng)計覆蓋率,那么go 項目如何進(jìn)行代碼覆蓋率測試呢,本文將給大家詳細(xì)的介紹一下golang使用go test輸出單元測試覆蓋率的方式,需要的朋友可以參考下
    2024-02-02
  • golang框架中跨服務(wù)的最佳通信協(xié)議和工具

    golang框架中跨服務(wù)的最佳通信協(xié)議和工具

    在 go 框架中實現(xiàn)跨服務(wù)通信的最佳實踐包括使用 grpc(適用于低延遲高吞吐量)、http 客戶端(適用于 restful api)和消息隊列(適用于異步解耦通信),在選擇通信方式時,應(yīng)考慮服務(wù)交互模式、性能要求和部署環(huán)境等因素
    2024-06-06
  • 解析golang 標(biāo)準(zhǔn)庫template的代碼生成方法

    解析golang 標(biāo)準(zhǔn)庫template的代碼生成方法

    這個項目的自動生成代碼都是基于 golang 的標(biāo)準(zhǔn)庫 template 的,所以這篇文章也算是對使用 template 庫的一次總結(jié),本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11
  • GoLang職責(zé)鏈模式代碼實現(xiàn)介紹

    GoLang職責(zé)鏈模式代碼實現(xiàn)介紹

    這篇文章主要介紹了GoLang職責(zé)鏈模式代碼實現(xiàn),職責(zé)鏈模式是一種常用的設(shè)計模式,可以提高代碼的靈活性與可維護(hù)性,職責(zé)鏈模式將請求和處理分離,可以讓請求在處理鏈中依次經(jīng)過多個處理者,直到找到能夠處理請求的處理者為止
    2023-05-05
  • Go語言中常見的文件操作分享

    Go語言中常見的文件操作分享

    文件操作應(yīng)該是應(yīng)用程序里非常常見的一種操作,無論是哪種應(yīng)用場景,幾乎都離不開文件的基本操作。Go語言中提供了三個不同的包去處理文件,下午就來說說它們的具體使用
    2023-01-01

最新評論