自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go1.23 新特性:花了近 10 年,time.After 終于不泄漏了!

開發(fā) 前端
今天給大家分享了一個花了將近 10 年,Go 才解決的計時器泄露問題。為此還是要給 rsc 點贊的,至少一直都有記著。就是這個解決速度比較慢,很多人在真實的 Go 工程中都已經(jīng)遇到過了。

大家好,我是煎魚。

好多年前,我寫過 timer.After 的使用和坑。Go 這么多年以來這塊一直有內(nèi)存泄露。有的同學(xué)或多或少都有遇到過。

最近 Go1.23 即將正式發(fā)布,Go 核心團(tuán)隊負(fù)責(zé)人 rsc 自述花了將近 10 年的努力,終于把這個問題修復(fù)了。值得我們關(guān)注!

timer.After 是什么

這是之前編寫的部分,我測試驗證了下。在 Go1.22 依然有效,仍然是有問題的。因此沒有做什么修改。主要是給大家做知識溫習(xí)回顧的作用。

今天是男主角是 Go 標(biāo)準(zhǔn)庫 time 所提供的 After 方法。函數(shù)簽名如下:

func After(d Duration) <-chan Time

該方法可以在一定時間(根據(jù)所傳入的 Duration)后主動返回 time.Time 類型的 channel 消息。

在常見的場景下,我們會基于此方法做一些計時器相關(guān)的功能開發(fā),例子如下:

func main() {
    ch := make(chan string)
    go func() {
        time.Sleep(time.Second * 3)
        ch <- "腦子進(jìn)煎魚了"
    }()

    select {
    case _ = <-ch:
    case <-time.After(time.Second * 1):
        fmt.Println("煎魚出去了,超時了?。?!")
    }
}

在運行 1 秒鐘后,輸出結(jié)果:

煎魚出去了,超時了?。?!

上述程序在在運行 1 秒鐘后將觸發(fā) time.After 方法的定時消息返回,輸出了超時的結(jié)果。

有什么問題和坑

從例子來看似乎非常正常,也沒什么 “坑” 的樣子。莫非是虛晃一槍?

我們再看一個不像是有問題例子,這在 Go 工程中經(jīng)常能看見,只是大家都沒怎么關(guān)注。

代碼如下:

func main() {
    ch := make(chan int, 10)
    go func() {
        in := 1
        for {
            in++
            ch <- in
        }
    }()

    for {
        select {
        case _ = <-ch:
            // 煎魚干了點什么...
            continue
        case <-time.After(3 * time.Minute):
            fmt.Printf("現(xiàn)在是:%d,我腦子進(jìn)煎魚了!", time.Now().Unix())
        }
    }
}

在上述代碼中,我們構(gòu)造了一個 for+select+channel 的一個經(jīng)典的處理模式。

同時在 select+case 中調(diào)用了 time.After 方法做超時控制,避免在 channel 等待時阻塞過久,引發(fā)其他問題。

看上去都沒什么問題,但是細(xì)心一看。在運行了一段時間后,我的筆記本電腦已經(jīng)溫?zé)崃嗽S多。

粗暴的利用 top 命令一看:

圖片圖片

例子中 Go 工程的內(nèi)存占用竟然已經(jīng)達(dá)到了 30+GB 之高,并且還在持續(xù)增長。在再等待了一段時間后(所設(shè)置的超時時間到達(dá)),Go 工程的內(nèi)存占用也沒有要恢復(fù)合理的數(shù)值。這非??膳?。

這明顯就是存在內(nèi)存泄露的問題。

問題原因

這個內(nèi)存泄露的問題,無容置疑是 Go 官方認(rèn)可的 BUG。

快速的用一句話來講,核心原因在于:for select 已結(jié)束,無法被 GC,時間堆內(nèi)的被觸發(fā)的計時器還在。

Go 官方文檔說明Go 官方文檔說明

如果是想深入看原因可以查看以前我寫的《Go 內(nèi)存泄露之痛,這篇把 Go timer.After 問題根因講透了!》

Go1.23 timer.After 不泄露了!

在現(xiàn)在 2024 年,經(jīng)過將近十年的努力,Go 核心團(tuán)隊負(fù)責(zé)人 rsc 終于解決了這個問題?。?!

圖片圖片

自 Go1.23 版本起,會對用于計時器的通道(或者可能是用于通道的計時器)進(jìn)行特殊處理,以便當(dāng)沒有通道操作待處理時,計時器將不會存放在計時器堆中。

這意味著當(dāng)一旦不再引用通道和計時器,就可以對其進(jìn)行 GC,不必等待計時器到期或明確停止計時器。

注:這里的計時器是指 time.After、time.NewTimer 和 time.NewTicker 使用的數(shù)據(jù)結(jié)構(gòu)。

測試和驗證

可能會有的同學(xué)會想體驗 Go1.23 的新特性,驗證這個 time.After 的修復(fù)是否有效。要特別注意下面這一點。

我們還是用前面提到的問題代碼來測試。但如果你直接在本地復(fù)用,可能不一定能生效,會看到還是有內(nèi)存泄露的情況。

主要是兩個原因,如下:

1、你要下載 Go 新版本并使用 Go1.23 運行:

// 安裝 go1.23rc2 的 go 新版本
$ go install golang.org/dl/go1.23rc2@latest
$ go1.23rc2 download

// 運行煎魚前面的代碼例子
$ go1.23rc2 run main.go

2、項目的 go.mod 文件注意 go 版本在 1.23,否則該新特性將由于兼容性保障無法生效:

圖片圖片

運行一段時間后,之前的代碼中 Go1.23rc2 下內(nèi)存情況基本正常:

圖片圖片

總結(jié)

今天給大家分享了一個花了將近 10 年,Go 才解決的計時器泄露問題。為此還是要給 rsc 點贊的,至少一直都有記著。就是這個解決速度比較慢,很多人在真實的 Go 工程中都已經(jīng)遇到過了。

另外從新版本開始,大家在舊項目體驗新特性是,要注意項目 go.mod 的 go 行版本或是 go toolchain 版本,避免由于版本過低而無法測試到真實的新特性效果。

責(zé)任編輯:武曉燕 來源: 腦子進(jìn)煎魚了
相關(guān)推薦

2024-08-07 08:51:20

Go優(yōu)化開發(fā)

2024-09-09 08:56:03

2024-08-20 08:51:41

2025-03-03 00:05:00

GoTimer調(diào)度器

2024-09-02 00:30:41

Go語言場景

2020-09-22 07:49:05

內(nèi)存泄漏

2021-08-30 10:49:39

Go語言編譯器

2018-10-31 12:41:11

2021-09-05 18:25:30

Go命令倉庫

2021-12-09 08:50:35

Kubernetes增強(qiáng)功能版本更新

2024-01-22 00:30:00

Go編程Go 1.22

2017-12-18 17:21:56

AndroidJava內(nèi)存泄漏

2024-09-02 10:21:21

2020-10-10 09:01:54

泄漏

2010-07-20 10:19:06

Wine 1.2

2025-01-21 15:10:36

2019-03-05 15:03:09

Android Q安卓系統(tǒng)功能

2022-02-11 21:01:18

GoNetip網(wǎng)絡(luò)庫

2022-06-30 06:00:30

Edge瀏覽器

2021-02-02 09:10:12

Go語言二進(jìn)制
點贊
收藏

51CTO技術(shù)棧公眾號