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

Go?語言垃圾回收機(jī)制從入門到理解

 更新時(shí)間:2025年09月03日 09:58:50   作者:小羊在睡覺  
本文主要介紹了Go?語言垃圾回收機(jī)制從入門到理解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言:為什么我們要聊 GC?

我們寫程序就像是在一個(gè)大房間里工作,每當(dāng)我們創(chuàng)建一個(gè)變量、一個(gè)對(duì)象,就等于往房間里放了一件家具。用完的家具,如果我們不及時(shí)處理,房間就會(huì)越來越亂,最終擠得連走路的地方都沒有。在編程世界里,這個(gè)“房間”就是內(nèi)存,而“垃圾”就是那些程序不再使用的內(nèi)存空間。

在 Go 語言中,我們不用自己去手動(dòng)清理這些“垃圾”,因?yàn)橛幸粋€(gè)勤勞的“清潔工”—— 垃圾回收器(Garbage Collector, GC) 會(huì)自動(dòng)完成這項(xiàng)工作。雖然它很勤快,但如果我們的程序?qū)懙貌粔蚝?,讓它忙不過來,也可能會(huì)影響程序的性能。所以,理解 GC 的工作原理,能幫助我們寫出更高效、更“干凈”的代碼。

1. 什么是垃圾回收?

在深入 Go 的 GC 之前,我們先來聊聊 GC 的基本概念。

什么是“垃圾”?

簡(jiǎn)單來說,“垃圾”就是 程序不再使用的內(nèi)存。

舉個(gè)例子:

package main
 
import "fmt"
 
func main() {
	var a int = 10
	{
		var b int = 20
		fmt.Println(b) // 在這里,變量 b 仍然有效
	} // b 的作用域結(jié)束,b 占用的內(nèi)存成為“垃圾”
 
	// 另一個(gè)例子:當(dāng)一個(gè)變量不再被引用時(shí)
	s := "hello world"
	fmt.Println(s)
	s = "go language" // 字符串 "hello world" 不再被任何變量引用,成為“垃圾”
	fmt.Println(s)
} // a 的作用域結(jié)束,a 占用的內(nèi)存成為“垃圾”
 
 
 
 

當(dāng)一個(gè)變量的作用域結(jié)束,或者沒有其他任何變量再指向它時(shí),它所占用的內(nèi)存就是可以被回收的“垃圾”了。

為什么需要 GC?

  • 減輕開發(fā)者的負(fù)擔(dān):在沒有 GC 的語言(比如 C++)中,開發(fā)者需要手動(dòng)分配和釋放內(nèi)存。這很容易出錯(cuò),比如忘記釋放內(nèi)存導(dǎo)致 內(nèi)存泄漏,或者重復(fù)釋放內(nèi)存導(dǎo)致程序崩潰。
  • 提高開發(fā)效率:有了 GC,開發(fā)者可以更專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),而不用花費(fèi)大量精力去管理內(nèi)存,大大提升了開發(fā)效率。

2. Go GC 的核心原理:三色標(biāo)記法

Go GC 的核心算法叫做 “三色標(biāo)記法”。聽起來像是在畫畫,沒錯(cuò),它的原理就是給程序中的所有對(duì)象“涂上”三種顏色。

三種顏色代表什么?

我們可以把程序中所有的內(nèi)存對(duì)象想象成一個(gè)個(gè)小方塊,GC 的任務(wù)就是給這些小方塊“涂色”,然后把白色的方塊清理掉。

  • 白色 (White): 初始狀態(tài),所有對(duì)象都是白色的。它們是 GC 眼中的 “潛在垃圾”。
  • 灰色 (Gray): 對(duì)象被 標(biāo)記 了,但它里面包含的引用(比如一個(gè)結(jié)構(gòu)體里的指針)還沒有被檢查。我們可以把灰色對(duì)象看作是“待處理”的對(duì)象。
  • 黑色 (Black): 對(duì)象被標(biāo)記了,并且它所引用的所有子對(duì)象也都被檢查過了。黑色對(duì)象就是 “確認(rèn)存活” 的對(duì)象。

三色標(biāo)記的流程

  1. 初始狀態(tài):所有對(duì)象都是白色的。
  2. 標(biāo)記階段 (Mark):GC 會(huì)從“根對(duì)象”開始遍歷,比如全局變量、當(dāng)前函數(shù)棧上的變量等。GC 會(huì)把這些根對(duì)象以及它們直接引用的對(duì)象標(biāo)記為灰色,并放入一個(gè)隊(duì)列。
  3. 循環(huán)檢查:GC 依次從灰色隊(duì)列中取出一個(gè)對(duì)象,把它標(biāo)記為黑色,然后檢查它所引用的所有對(duì)象。如果引用的對(duì)象是白色的,就把它標(biāo)記為灰色并加入隊(duì)列。
  4. 最終清理 (Sweep):當(dāng)灰色隊(duì)列變空,GC 就知道所有存活的對(duì)象都被標(biāo)記成黑色了。這時(shí),GC 就會(huì)遍歷整個(gè)內(nèi)存,把所有還停留在 白色 的對(duì)象全部回收掉。

3. Go GC 的演進(jìn):從“暫停世界”到“并發(fā)執(zhí)行”

早期的 GC 算法有一個(gè)很大的缺點(diǎn),叫做 STW (Stop-The-World)。

什么是 STW?

  • 在 STW 模式下,GC 運(yùn)行時(shí),程序會(huì)完全暫停,不能做任何事情。
  • 這就好比清潔工來打掃房間時(shí),你必須停下所有工作,坐在椅子上不動(dòng),等他打掃完了你才能繼續(xù)。
  • 缺點(diǎn):如果你的程序內(nèi)存很大,GC 暫停的時(shí)間就會(huì)很長,這會(huì)嚴(yán)重影響程序的性能,尤其是在高并發(fā)的服務(wù)器應(yīng)用中,用戶可能會(huì)感受到明顯的卡頓。

Go 語言的 GC 團(tuán)隊(duì)也意識(shí)到了這個(gè)問題,并進(jìn)行了一系列優(yōu)化。

Go 1.5 之后:Go 語言引入了 并發(fā) GC。

  • 什么是并發(fā) GC? 顧名思義,就是 GC 的大部分工作可以 和程序同時(shí)進(jìn)行
  • 這大大減少了 STW 的暫停時(shí)間,讓 GC 幾乎不會(huì)影響到程序的正常運(yùn)行。

Go 1.8 之后:Go GC 進(jìn)一步優(yōu)化,現(xiàn)在的 STW 暫停時(shí)間已經(jīng)非常短,通常在微秒級(jí)別,幾乎可以忽略不計(jì)。

4. 如何觀察和優(yōu)化 Go GC?

既然 GC 是自動(dòng)的,那我們還需要關(guān)心它嗎?當(dāng)然!理解 GC 的工作,可以幫助我們更好地診斷和解決性能問題。

如何查看 GC 信息?

  • 你可以在運(yùn)行 Go 程序時(shí),通過設(shè)置環(huán)境變量來查看詳細(xì)的 GC 日志。
  • 讓我們寫一個(gè)簡(jiǎn)單的程序,它會(huì)持續(xù)分配內(nèi)存:
package main

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	// 打印 GC 狀態(tài)
	go func() {
		for {
			var m runtime.MemStats
			runtime.ReadMemStats(&m)
			fmt.Printf("當(dāng)前已分配內(nèi)存: %v MB, 下次GC內(nèi)存閾值: %v MB\n", m.Alloc/1024/1024, m.NextGC/1024/1024)
			time.Sleep(2 * time.Second)
		}
	}()

	// 持續(xù)分配內(nèi)存
	var a []byte
	for i := 0; i < 10; i++ {
		// 每次分配 100MB 內(nèi)存
		a = append(a, make([]byte, 100*1024*1024)...)
		fmt.Printf("第 %d 次分配內(nèi)存完成\n", i+1)
		time.Sleep(1 * time.Second)
	}
}

運(yùn)行這個(gè)程序,同時(shí)設(shè)置 GODEBUG=gctrace=1 環(huán)境變量,例如: GODEBUG=gctrace=1 go run your_program.go

運(yùn)行后,除了我們自己打印的內(nèi)存信息,你還會(huì)看到類似下面的 GC 日志:

gc 1 @0.038s 0%: 0.054+1.4+0.007 ms clock, 0.43+0.32/1.4/0.38+0.05 ms cpu, 4->4 MB, 10->10 MB, 4 (2) objects, 1 (0) goroutines, 0/0/0/0 ms inter-sweep, 0/0(G)/0(H) MSpans, ...
  • 日志中會(huì)包含 GC 的 ID、觸發(fā)時(shí)間、STW 暫停時(shí)間、回收的內(nèi)存量等關(guān)鍵信息。

如何減少 GC 壓力?

  • 減少內(nèi)存分配:每次 new 或 make 都會(huì)增加 GC 的工作量。盡量復(fù)用對(duì)象,減少不必要的內(nèi)存分配。
  • 使用 sync.Pool:對(duì)于那些創(chuàng)建和銷毀都非常頻繁的小對(duì)象,可以使用 sync.Pool 來緩存和復(fù)用它們,大大減輕 GC 的壓力。

sync.Pool 示例:

package main

import (
	"fmt"
	"sync"
	"time"
)

// 定義一個(gè)需要被頻繁創(chuàng)建和銷毀的對(duì)象
type Data struct {
	ID   int
	Name string
}

func main() {
	// 創(chuàng)建一個(gè) sync.Pool,并定義 New 函數(shù),用于創(chuàng)建新的對(duì)象
	dataPool := &sync.Pool{
		New: func() interface{} {
			fmt.Println("創(chuàng)建了一個(gè)新的 Data 對(duì)象")
			return &Data{}
		},
	}

	// 不使用 sync.Pool,每次都創(chuàng)建新對(duì)象
	fmt.Println("--- 不使用 sync.Pool ---")
	for i := 0; i < 3; i++ {
		_ = &Data{ID: i}
		time.Sleep(100 * time.Millisecond)
	}

	// 使用 sync.Pool,從池中獲取對(duì)象
	fmt.Println("\n--- 使用 sync.Pool ---")
	for i := 0; i < 3; i++ {
		// Get() 方法會(huì)嘗試從池中獲取一個(gè)對(duì)象,如果池為空,則會(huì)調(diào)用 New()
		obj := dataPool.Get().(*Data)
		obj.ID = i
		obj.Name = fmt.Sprintf("數(shù)據(jù) %d", i)
		fmt.Printf("使用對(duì)象: %+v\n", obj)

		// Put() 方法會(huì)將對(duì)象放回池中,供下次復(fù)用
		dataPool.Put(obj)
		time.Sleep(100 * time.Millisecond)
	}

	// 再次獲取,這次會(huì)直接從池中復(fù)用,而不會(huì)調(diào)用 New()
	fmt.Println("\n--- 再次使用 sync.Pool ---")
	obj := dataPool.Get().(*Data)
	fmt.Printf("復(fù)用對(duì)象: %+v\n", obj)
}




合理設(shè)置 GOGCGOGC 是一個(gè)環(huán)境變量,可以用來控制 GC 的觸發(fā)時(shí)機(jī)。默認(rèn)值為 100,表示當(dāng)新分配的內(nèi)存達(dá)到上次 GC 之后存活內(nèi)存的 100% 時(shí),就會(huì)觸發(fā)新一輪 GC。你可以根據(jù)程序的特點(diǎn),適當(dāng)調(diào)整這個(gè)值。

總結(jié)

Go GC 的設(shè)計(jì)理念是 并發(fā)、低延遲、自動(dòng)管理。作為 Go 開發(fā)者,雖然我們不用手動(dòng)管理內(nèi)存,但理解 GC 的工作原理依然非常重要。它能幫助我們寫出更高效的程序,更輕松地應(yīng)對(duì)各種性能挑戰(zhàn)。

到此這篇關(guān)于Go 語言垃圾回收機(jī)制從入門到理解的文章就介紹到這了,更多相關(guān)Go垃圾回收機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言中 Channel 詳解

    Go語言中 Channel 詳解

    Go 語言中的 channel 是實(shí)現(xiàn) goroutine 間無鎖通信的關(guān)鍵機(jī)制,他使得寫多線程并發(fā)程序變得簡(jiǎn)單、靈活、觸手可得。下面就個(gè)人理解對(duì) channel 使用過程中應(yīng)該注意的地方進(jìn)行一個(gè)簡(jiǎn)要的總結(jié)。
    2018-10-10
  • 解讀golang中的const常量和iota

    解讀golang中的const常量和iota

    這篇文章主要介紹了golang中的const常量和iota,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • golang國內(nèi)proxy設(shè)置方式

    golang國內(nèi)proxy設(shè)置方式

    建議使用goproxy.cn作為Go包鏡像,因其速度快且緩存豐富,若遇下載問題可更換源,同時(shí)注意GitHub刪除的包可能在goproxy.io中緩存
    2025-07-07
  • 簡(jiǎn)單了解Go語言中函數(shù)作為值以及函數(shù)閉包的使用

    簡(jiǎn)單了解Go語言中函數(shù)作為值以及函數(shù)閉包的使用

    這篇文章主要介紹了簡(jiǎn)單了解Go語言中函數(shù)作為值以及函數(shù)閉包的使用,是golang入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • golang實(shí)現(xiàn)跨域訪問的方法

    golang實(shí)現(xiàn)跨域訪問的方法

    這篇文章主要介紹了golang實(shí)現(xiàn)跨域訪問的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • golang gorm多條件篩選查詢操作

    golang gorm多條件篩選查詢操作

    這篇文章主要介紹了golang gorm多條件篩選查詢操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go-客戶信息關(guān)系系統(tǒng)的實(shí)現(xiàn)

    Go-客戶信息關(guān)系系統(tǒng)的實(shí)現(xiàn)

    這篇文章主要介紹了Go-客戶信息關(guān)系系統(tǒng)的實(shí)現(xiàn),本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,需要的朋友可以參考下
    2023-01-01
  • go打包aar及flutter調(diào)用aar流程詳解

    go打包aar及flutter調(diào)用aar流程詳解

    這篇文章主要為大家介紹了go打包aar及flutter調(diào)用aar流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • go mod 使用私有g(shù)itlab群組的解決方案

    go mod 使用私有g(shù)itlab群組的解決方案

    這篇文章主要介紹了go mod 使用私有g(shù)itlab群組的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • 用go gin server來做文件上傳服務(wù)

    用go gin server來做文件上傳服務(wù)

    今天小編就為大家分享一篇關(guān)于用go gin server來做文件上傳服務(wù),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04

最新評(píng)論