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

go語言內存泄漏的常見形式

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

go語言內存泄漏

在這里插入圖片描述

子字符串導致的內存泄漏

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

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

例如如下代碼,一旦調用demo就會導致將近1M內存的泄漏,因為s0只使用了50字節(jié),但是會導致1M的內存一直無法被回收,這些內存會一直持續(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)
}

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

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

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

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

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

避免此類內存泄漏的第三種方法是利用 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 標準庫包中新增了 Clone 函數,這成為了完成這項工作的最佳方式。

子切片導致的內存泄漏

同樣場景下,切片也會導致內存的浪費

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

var s0 []int

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

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

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.
}

未重置子切片指針導致的內存泄漏

在下面的代碼中,調用 h 函數后,為切片 s 的第一個和最后一個元素分配的內存塊將丟失。

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 值分配的兩個內存塊。

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

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導致的內存泄漏

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

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

如果不停止time.Ticker也會導致內存泄漏

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

不正確地使用終結器會導致真正的內存泄漏

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

例如,在調用并退出以下函數后,分配給 x 和 y 的內存塊不能保證在未來的垃圾收集中被收集。

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)引用組中的值設置終結器。

延遲函數調用導致的某種資源泄漏

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

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

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
}

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

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
}

當然不要犯以下錯誤,需要有些同學將需要延時調用的函數字節(jié)省略,導致資源泄漏

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

如果是http請求,還會導致服務端擠壓大量的連接無法釋放

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

相關文章

  • Go-RESTful實現下載功能思路詳解

    Go-RESTful實現下載功能思路詳解

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

    go語言實現依賴注入的示例代碼

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

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

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

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

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

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

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

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

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

    golang框架中跨服務的最佳通信協議和工具

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

    解析golang 標準庫template的代碼生成方法

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

    GoLang職責鏈模式代碼實現介紹

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

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

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

最新評論