golang語言中的意想不到

2022-05-08 11:21:08 字數 2444 閱讀 1773

編譯器會自動選擇在棧上還是在堆上分配區域性變數的儲存空間,但可能令人驚訝的是,這個選擇並不是由用var還是new宣告變數的方式決定的。在go語言規範中甚至故意沒有講到棧和堆的概念。我們無法知道函式引數或區域性變數到底是儲存在棧中還是堆中,編譯器和執行時會幫我們搞定這個變數是在;同樣不要假設變數在記憶體中的位置是固定不變的,指標隨時可能會變化,特別是在你不期望它變我們只需要知道它們能夠正常工作就可以了。看看下面這個例子:

var global *int

func f()

func g()

f函式裡的x變數必須在堆上分配,因為它在函式退出後依然可以通過包一級的global變數找到,雖然它是在函式內部定義的;用go語言的術語說,這個x區域性變數從函式f中逃逸了。相反,當g函式返回時,變數 *y 將是不可達的,也就是說可以馬上被**的。因此, *y 並沒有從函式g中逃逸,編譯器可以選擇在棧上分配 *y 的儲存空間(譯註:也可以選擇在堆上分配,然後由go語言的gc**這個變數的記憶體空間),雖然這裡用的是new方式。其實在任何時候,你並不需為了編寫正確的**而要考慮變數的逃逸行為,要記住的是,逃逸的變數需要額外分配記憶體,同時對效能的優化可能會產生細微的影響。go語言的自動垃圾收集器對編寫正確的**是乙個巨大的幫助,但也並不是說你完全不用考慮記憶體了。你雖然不需要顯式地分配和釋放記憶體,但是要編寫高效的程式你依然需要了解變數的生命週期。例如,如果將指向短生命週期物件的指標儲存到具有長生命週期的物件中,特別是儲存到全域性變數時,會阻止對短生命週期物件的垃圾**(從而可能影響程式的效能)。

眾所周知,go是自動垃圾**的(garbage collector),這大大減少了程式程式設計負擔。但gc是一把雙刃劍,帶來了程式設計的方便但同時也增加了執行時開銷,使用不當甚至會嚴重影響程式的效能。因此效能要求高的場景不能任意產生太多的垃圾,如何解決呢?那就是要重用物件了,我們可以簡單的使用乙個chan把這些可重用的物件快取起來,但如果很多goroutine競爭乙個chan效能肯定是問題.....由於golang團隊認識到這個問題普遍存在,為了避免大家重造車輪,因此官方統一出了乙個包pool。放到sync包裡面大概是因為它也是基於共享變數的併發方法之一。pool的使用方法如下:

package main

import(

"fmt"

"sync")

func main() ,

}a := p.get().(int)

p.put(1)

b := p.get().(int)

fmt.println(a, b)

}

上面建立了乙個快取int物件的乙個pool,先從池獲取乙個物件然後放進去乙個物件再取出乙個物件,程式的輸出是0 1。建立的時候可以指定乙個new函式,獲取物件的時候如何在池裡面找不到快取的物件將會使用指定的new函式建立乙個返回,如果沒有new函式則返回nil.既然是快取的pool,那麼這個pool的快取有沒有長度限制?是否是執行緒安全的呢?開銷如何呢?

func init()
可以看到pool包在init的時候註冊了乙個poolcleanup函式,它會清除所有的pool裡面的所有快取的物件,該函式註冊進去之後會在每次gc之前都會呼叫,因此sync.pool快取的期限只是兩次gc之間這段時間。例如我們把上面的例子改成下面這樣之後,輸出的結果將是0 0。正因gc的時候會清掉快取物件,也不用擔心pool會無限增大的問題。

a := p.get().(int)

p.put(1)

runtime.gc()

b := p.get().(int)

fmt.println(a, b)

這是很多人錯誤理解的地方,正因為這樣,我們是不可以使用sync.pool去實現乙個socket連線池的。

獲取物件過程是:

固定到某個p,嘗試從私有物件獲取,如果私有物件非空則返回該物件,並把私有物件置空;

如果私有物件是空的時候,就去當前子池的共享列表獲取(需要加鎖);

如果當前子池的共享列表也是空的,那麼就嘗試去其他p的子池的共享列表偷取乙個(需要加鎖);

如果其他子池都是空的,最後就用使用者指定的new函式產生乙個新的物件返回。

可以看到一次get操作最少0次加鎖,最大n(n等於maxprocs)次加鎖。

歸還物件的過程:

固定到某個p,如果私有物件為空則放到私有物件;

否則加入到該p子池的共享列表中(需要加鎖)。

可以看到一次put操作最少0次加鎖,最多1次加鎖。

由於goroutine具體會分配到那個p執行是golang的協程排程系統決定的,因此在maxprocs>1的情況下,多goroutine用同乙個sync.pool的話,各個p的子池之間快取的物件是否平衡以及開銷如何是沒辦法準確衡量的。但如果goroutine數目和快取的物件數目遠遠大於maxprocs的話,概率上說應該是相對平衡的。

總的來說,sync.pool的定位不是做類似連線池的東西,它的用途僅僅是增加物件重用的機率,減少gc的負擔,而開銷方面也不是很便宜的。

意想不到的有趣linux命令

1.sl 命令 sl是指 steam locomotive 蒸汽機車 你會看到一輛火車從螢幕右邊開往左邊。安裝 sudo apt get install sl 執行 sl 命令有 alfe幾個選項,可以給別人來個惡作劇,搞個關聯,他一敲ls,不知道的肯定很有效果 alias ls sl 2.fort...

意想不到的有趣linux命令

1.sl 命令 sl是指 steam locomotive 蒸汽機車 你會看到一輛火車從螢幕右邊開往左邊。安裝 sudo apt get install sl 執行 sl 命令有 alfe幾個選項,可以給別人來個惡作劇,搞個關聯,他一敲ls,不知道的肯定很有效果 alias ls sl 2.fort...

意想不到的單詞結構化

在之前印象裡,單詞這種東西怎麼也和結構化沾不上邊,直到上了公尺老師的課,才第一次深刻感受到,單詞是不需要去背的,通過結構化進行分析,分析意思的起源和之間的聯絡,曾經枯燥的背單詞過程,變成了乙個有故事的過程,不再枯燥,也大大提高了效率。study,往常僅僅知道 學習 這乙個意思,其他的意思字典上當然有...