熟悉golang關(guān)鍵點(diǎn)總結(jié)

1.gc垃圾回收算法:標(biāo)記-清除法

基本原理:從根(包括全局指針以及goroutine棧上指針)出發(fā),標(biāo)記可達(dá)節(jié)點(diǎn)為灰色,然后是繼續(xù)標(biāo)記灰色節(jié)點(diǎn)可達(dá)的節(jié)點(diǎn)為灰色肠阱,同時(shí)自身由灰色變?yōu)楹谏扇危粩嘀貜?fù),剩下所有的白色節(jié)點(diǎn)為垃圾回收節(jié)點(diǎn)

2.減輕gc負(fù)擔(dān)的根本方法是:減少對(duì)象數(shù)量,特別是減少小對(duì)象的大量創(chuàng)建
3.goroutine調(diào)度原理:半搶占式(在函數(shù)入口處插入?yún)f(xié)作代碼)的協(xié)作調(diào)度,可以理解為公平協(xié)作調(diào)度和搶占式調(diào)度的結(jié)合體

GPM模型,一個(gè)M對(duì)應(yīng)一個(gè)P對(duì)應(yīng)一個(gè)多個(gè)G的隊(duì)列蛉签,外加一個(gè)G的全局隊(duì)列
G被調(diào)度的時(shí)機(jī)有以下幾種場(chǎng)景:

a. 簡(jiǎn)單搶占式調(diào)度:一旦某個(gè)G中出現(xiàn)死循環(huán)或永久循環(huán)的代碼邏輯,那么G將永久占用分配給它的P和M沥寥,位于同一個(gè)P中的其他G將得不到調(diào)度碍舍,出現(xiàn)“餓死”的情況,在Go 1.2中實(shí)現(xiàn)了搶占式調(diào)度來解決這個(gè)問題邑雅,原理則是在每個(gè)函數(shù)或方法的入口片橡,加上一段額外的代碼,讓runtime有機(jī)會(huì)檢查是否需要執(zhí)行搶占調(diào)度(是否需要調(diào)度的判斷標(biāo)準(zhǔn)是由獨(dú)立的sysmon線程來維護(hù)的)蒂阱。這種解決方案只能說局部解決了“餓死”問題锻全,對(duì)于沒有函數(shù)調(diào)用,純算法循環(huán)計(jì)算的G录煤,scheduler依然無法搶占鳄厌。

b. channel阻塞、鎖阻塞( atomic, mutex, 或者 channel)等阻塞場(chǎng)景:如果G被阻塞在某個(gè)channel操作或者G被阻塞到同步互斥操作妈踊,也就是 Lock()了嚎,Unlock() 等鎖的情況下,G會(huì)被放置到某個(gè)wait隊(duì)列中,原來的M不會(huì)阻塞歪泳,會(huì)嘗試運(yùn)行下一個(gè)runnable的G萝勤,當(dāng)被放在wait隊(duì)列中的G不再阻塞時(shí),可以將G重新放到隊(duì)列中等待調(diào)度執(zhí)行

c. 異步system call 例如network I/O情況下的調(diào)度:當(dāng)G進(jìn)行network I/O操作時(shí)呐伞,G會(huì)被放置到某個(gè)wait隊(duì)列中敌卓,而M會(huì)嘗試運(yùn)行下一個(gè)runnable的G,當(dāng)被放在wait隊(duì)列中的G不再阻塞時(shí)伶氢,可以將G重新放到隊(duì)列中等待調(diào)度執(zhí)行

c. 同步system call例如文件io操作阻塞情況下的調(diào)度:如果G被阻塞在某個(gè)system call操作上趟径,那么不光G會(huì)阻塞,執(zhí)行該G的M也會(huì)解綁P(實(shí)質(zhì)是被sysmon搶走了)癣防,與G一起進(jìn)入sleep狀態(tài)蜗巧。如果此時(shí)有idle的M,則P與其綁定繼續(xù)執(zhí)行其他G蕾盯;如果沒有idle M幕屹,但仍然有其他G要去執(zhí)行,那么就會(huì)創(chuàng)建一個(gè)新M级遭。當(dāng)阻塞在syscall上的G完成syscall調(diào)用后望拖,G會(huì)去嘗試獲取一個(gè)可用的P,如果沒有可用的P装畅,那么G會(huì)被標(biāo)記為runnable靠娱,之前的那個(gè)sleep的M將再次進(jìn)入sleep。Go語言完全是自己封裝的系統(tǒng)調(diào)用掠兄,所以在封裝系統(tǒng)調(diào)用的時(shí)候,可以做不少手腳锌雀,也就是進(jìn)入系統(tǒng)調(diào)用的時(shí)候執(zhí)行entersyscall蚂夕,退出后又執(zhí)行exitsyscall函數(shù)。 也只有封裝了entersyscall的系統(tǒng)調(diào)用才有可能觸發(fā)重新調(diào)度腋逆。

4.slice與數(shù)組:slice是引用類型婿牍,數(shù)組是值類型,slice可以動(dòng)態(tài)擴(kuò)容惩歉,減少動(dòng)態(tài)內(nèi)存拷貝可以預(yù)先分配
5.channel原理:golang參考CSP模型實(shí)現(xiàn)的類似管道的結(jié)構(gòu)等脂,本質(zhì)是一個(gè)循環(huán)隊(duì)列+鎖+兩個(gè)Goroutine等待隊(duì)列,channel是不同goroutine間通信的消息通道

channel引起的panic場(chǎng)景:

a. 關(guān)閉一個(gè)未初始化(nil) 的 channel 撑蚌;
b. 重復(fù)關(guān)閉同一個(gè) channel上遥;
c. 向一個(gè)已關(guān)閉的 channel 中發(fā)送消息;

從一個(gè)已關(guān)閉的 channel 中讀取消息永遠(yuǎn)不會(huì)阻塞争涌,并且會(huì)返回一個(gè)為 false 的 ok標(biāo)記粉楚,可以用它來判斷 channel 是否關(guān)閉。
單向 channel 一般是在聲明時(shí)會(huì)用到,防止傳遞進(jìn)函數(shù)的channel被隨意讀取獲取寫入模软,限制操作伟骨,比如

func foo(ch chan<- int) <-chan int {...}

chan<- int 表示一個(gè)只可寫入的 channel,<-chan int 表示一個(gè)只可讀取的 channel燃异。上面這個(gè)函數(shù)約定了 foo 內(nèi)只能從向 ch 中寫入數(shù)據(jù)携狭,返回只一個(gè)只能讀取的 channel,這樣在方法聲明時(shí)約定可以防止 channel 被濫用回俐,這種約定在編譯期間就確定下來了逛腿。
channel的實(shí)現(xiàn)原理:channel內(nèi)部主要組成:一個(gè)環(huán)形數(shù)組實(shí)現(xiàn)的隊(duì)列,用于存儲(chǔ)消息元素鲫剿;兩個(gè)鏈表實(shí)現(xiàn)的 goroutine 等待隊(duì)列鳄逾,用于存儲(chǔ)阻塞在 recv 和 send 操作上的 goroutine;一個(gè)互斥鎖灵莲,用于各個(gè)屬性變動(dòng)的同步雕凹。

6.golang內(nèi)存泄漏場(chǎng)景:

a. 永遠(yuǎn)處于阻塞狀態(tài)的goroutine,這將導(dǎo)致這些 goroutine 中使用的許多代碼塊永遠(yuǎn)無法進(jìn)行垃圾收集政冻,如下場(chǎng)景案例:
如果將以下函數(shù)作為 goroutine 的啟動(dòng)函數(shù)并將一個(gè) nil channel 參數(shù)傳遞給它, 則 goroutine 將永遠(yuǎn)阻塞. Go 運(yùn)行時(shí)認(rèn)為 goroutine 仍然存活, 所以為 s 分配的內(nèi)存塊將永遠(yuǎn)不會(huì)被收集.

func k(c <-chan bool) {
    s := make([]int64, 1e6)
    if <-c { // 如果 c 為 nil, 這里將永遠(yuǎn)阻塞
        _ = s
        // 使用 s, ...
    }
}

b.終結(jié)器(Finalizers):為循環(huán)引用組內(nèi)的成員設(shè)置 finalizer 可能會(huì)阻止為這個(gè)循環(huán)引用組分配的所有內(nèi)存塊被收集枚抵。
在下列函數(shù)被調(diào)用并退出之后, 為 x和 y 分配的內(nèi)存塊不保證在未來會(huì)被垃圾收集器回收.

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

    var finalizer = func(t *T) {
         fmt.Println("finalizer called")
    }
    
    var x, y T
    
    // SetFinalizer 會(huì)使 x 逃逸到堆上.
    runtime.SetFinalizer(&x, finalizer)
    
    // 以下語句將導(dǎo)致 x 和 y 變得無法收集.
    x.t, y.t = &y, &x // y 也逃逸到了 堆上.
}
7.interface與nil
go 中的接口分為兩種 
var Xxxx interface{}
type Xxxx  interface {
    Show()
}
底層結(jié)構(gòu):
type eface struct {      //空接口
    _type *_type         //類型信息
    data  unsafe.Pointer //指向數(shù)據(jù)的指針(go語言中特殊的指針類型unsafe.Pointer類似于c語言中的void*)
}
type iface struct {      //帶有方法的接口
    tab  *itab           //存儲(chǔ)type信息還有結(jié)構(gòu)實(shí)現(xiàn)方法的集合
    data unsafe.Pointer  //指向數(shù)據(jù)的指針(go語言中特殊的指針類型unsafe.Pointer類似于c語言中的void*)
}
data指向了nil 并不代表interface 是nil
8.golang內(nèi)存分配原理

new與make最終調(diào)用的都是mallocgc函數(shù)來進(jìn)行分配內(nèi)存,在啟動(dòng)時(shí)向操作系統(tǒng)預(yù)分配大塊內(nèi)存明场,內(nèi)存分配以TCMalloc的結(jié)構(gòu)進(jìn)行設(shè)計(jì), 不同大小的對(duì)象以不同class的span進(jìn)行分配,減少內(nèi)存碎片, 每個(gè)P都有cache減少鎖沖突汽摹,同時(shí)還有zero分配采用同一個(gè)全局對(duì)象, tiny分配器加快小對(duì)象分配的優(yōu)化。

9.切片會(huì)導(dǎo)致整個(gè)底層數(shù)組被鎖定

切片會(huì)導(dǎo)致整個(gè)底層數(shù)組被鎖定苦锨,底層數(shù)組無法釋放內(nèi)存逼泣。如果底層數(shù)組較大會(huì)對(duì)內(nèi)存產(chǎn)生很大的壓力。

func main() {
    headerMap := make(map[string][]byte)

    for i := 0; i < 5; i++ {
        name := "/path/to/file"
        data, err := ioutil.ReadFile(name)
        if err != nil {
            log.Fatal(err)
        }
        headerMap[name] = data[:1]
    }

    // do some thing
}

解決的方法是將結(jié)果克隆一份舟舒,這樣可以釋放底層的數(shù)組:

func main() {
    headerMap := make(map[string][]byte)

    for i := 0; i < 5; i++ {
        name := "/path/to/file"
        data, err := ioutil.ReadFile(name)
        if err != nil {
            log.Fatal(err)
        }
        headerMap[name] = append([]byte{}, data[:1]...)
    }

    // do some thing
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拉庶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子秃励,更是在濱河造成了極大的恐慌氏仗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺鲜,死亡現(xiàn)場(chǎng)離奇詭異皆尔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)币励,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門慷蠕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人榄审,你說我怎么就攤上這事砌们。” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵浪感,是天一觀的道長(zhǎng)昔头。 經(jīng)常有香客問我,道長(zhǎng)影兽,這世上最難降的妖魔是什么揭斧? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮峻堰,結(jié)果婚禮上讹开,老公的妹妹穿的比我還像新娘。我一直安慰自己捐名,他們只是感情好旦万,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镶蹋,像睡著了一般成艘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贺归,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天淆两,我揣著相機(jī)與錄音,去河邊找鬼拂酣。 笑死秋冰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的婶熬。 我是一名探鬼主播剑勾,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赵颅!你這毒婦竟也來了甥材?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤性含,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鸳惯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體商蕴,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年芝发,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绪商。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辅鲸,死狀恐怖格郁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤例书,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布锣尉,位于F島的核電站,受9級(jí)特大地震影響决采,放射性物質(zhì)發(fā)生泄漏自沧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一树瞭、第九天 我趴在偏房一處隱蔽的房頂上張望拇厢。 院中可真熱鬧,春花似錦晒喷、人聲如沸孝偎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衣盾。三九已至,卻和暖如春荡陷,著一層夾襖步出監(jiān)牢的瞬間雨效,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工废赞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徽龟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓唉地,卻偏偏與公主長(zhǎng)得像据悔,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子耘沼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容