為什么要寫這本書
因?yàn)镚o語言在服務(wù)端的開發(fā)效率、服務(wù)性能有著不俗的表現(xiàn),近幾年,Go 的熱度越來越高。國內(nèi)外很多大公司都在大規(guī)模地使用Go。Google就不用說了,它是Go語言誕生的地方,其他公司如Meta(Facebook)、uber、騰訊、字節(jié)跳動(dòng)、知乎、脈脈等都在擁抱和轉(zhuǎn)向Go。用Go 語言開發(fā)的著名開源項(xiàng)目也非常多,如k8s、docker、etcd、consul,每個(gè)名字都是如雷貫耳。
隨著市場對Go語言人才需求的不斷增長,很多開發(fā)人員都從其他語言,如PHP、C 、Java等轉(zhuǎn)投Go語言的懷抱。因?yàn)镚o語言自身的特點(diǎn)和優(yōu)勢,這些轉(zhuǎn)型的開發(fā)人員也能寫出性能不錯(cuò)的代碼。但是,由于沒有深入系統(tǒng)地學(xué)習(xí)Go的底層原理,在某些場景下,因?yàn)椴欢讓釉,無法快速定位問題、無法進(jìn)行性能優(yōu)化。
有些人說,語言并不重要,架構(gòu)、技術(shù)選型這些才是根本。筆者覺得這個(gè)說法不完全對,架構(gòu)、技術(shù)選型固然重要,但語言其實(shí)是開發(fā)人員每天都要打交道的東西,會(huì)用是遠(yuǎn)遠(yuǎn)不夠的,只有用好、知其所以然才能更全面地發(fā)揮其威力。
筆者自己親身經(jīng)歷的一個(gè)事故是關(guān)于Go 1.14之前調(diào)度器的一個(gè)坑:執(zhí)行無限循環(huán)且沒有函數(shù)調(diào)用的 goroutine無法被搶占,導(dǎo)致程序表現(xiàn)出死機(jī)。因?yàn)橹拔覍@個(gè)坑的原理已經(jīng)非常熟悉了。所以在事故現(xiàn)場,時(shí)間就明確了原因。后續(xù)的工作就是排查問題代碼,非常輕松。有些讀者要問了,既然你知道了坑的原理,為何還會(huì)掉進(jìn)去?我只能說Bug是必然存在的,只是發(fā)現(xiàn)的早晚而已。有Bug不可怕,怕的是發(fā)現(xiàn)Bug卻無法定位出來。
當(dāng)越來越多的開發(fā)人員都轉(zhuǎn)向Go語言時(shí),如何在眾多求職者中脫穎而出便成了面試官和求職者共同面臨的一個(gè)問題。早期從其他語言轉(zhuǎn)過來的開發(fā)人員,以為只要簡單學(xué)習(xí)Go語言,能寫出可運(yùn)行的代碼就可以了。但現(xiàn)在競爭越來越激烈,懂原理和只會(huì)寫代碼的人馬上就能被區(qū)分出來,那些抱殘守缺,秉承會(huì)用就行的理念的求職者,除非你在其他方面有出色的能力,否則你在職場上的競爭力就會(huì)很低。
現(xiàn)在網(wǎng)上流傳了很多看代碼打印結(jié)果的題目,我想說的是,這是把人腦當(dāng)成了編譯器嗎?面試不是背八股文,不是記語言點(diǎn):不用記住Go語言里的運(yùn)算符的優(yōu)先級,不需要看出這個(gè)變量是否逃逸到了堆上,也不用背Go GC經(jīng)歷了哪些階段……你只需要研究清楚它的原理,面試官問你什么問題就都能應(yīng)對。
近一兩年,筆者在中文世界論壇里發(fā)表了很多篇與Go源碼閱讀相關(guān)的文章,也是在寫作本書的過程中做的功課。我通過看源碼、做實(shí)驗(yàn)、請教大牛,對Go的理解逐漸加深。再去看很多文章就會(huì)感覺非常簡單,為什么這些我都能掌握?因?yàn)槲已芯窟^,我知道原理是什么,所以也知道你想要說什么。
后,希望通過本書,能讓你的Go水平真正上升一個(gè)臺(tái)階。
天道酬勤,與君共勉!
讀者交流及本書勘誤
由于篇幅有限,本書不可能涵蓋Go的所有內(nèi)容,但關(guān)鍵的內(nèi)容都呈現(xiàn)出來了,讀者有擴(kuò)展閱讀及資源獲取需求,可加入猿媛之家讀者服務(wù)QQ群(496588733)進(jìn)行交流。
本書為讀者提供了780分鐘的Go核心知識點(diǎn)講解,讀者可登錄網(wǎng)站https://golang.design/go- questions/獲取,同時(shí)本書后續(xù)的勘誤也將在該網(wǎng)站提供。讀者也可以關(guān)注下方公眾號進(jìn)行批評指正。
本書的讀者對象
無論你是面試官,還是求職者,這本書都能讓你有所收獲。另外,本書內(nèi)容不僅僅是對面試有幫助,所有寫Go的程序員都能從本書中有所收獲。
致謝
在寫作本書的過程中,和另一位學(xué)者歐長坤有很多交流討論,歐長坤是在讀博士,他對Go的理解非常深,他同時(shí)也是Go Contributor,我們的交流和討論讓我對很多問題有了更深入的理解,非常感謝。
我從Go夜讀社區(qū)的分享中學(xué)到了很多東西。并且我本人也擔(dān)任講師,分享了三期Go相關(guān)的內(nèi)容,很多觀眾都表示很有幫助。教是好的學(xué),我本人的收獲是多的。感謝Go夜讀社區(qū)的發(fā)起者楊文和SIG小組成員。
另外,我和Go圈的很多博客作者也有很多交流,收獲良多,在此一并感謝。
這兩年,我在碼農(nóng)桃花源發(fā)表了很多文章,得到了很多讀者的肯定,這也是我能不斷寫作的動(dòng)力,感謝你們。
饒全成
前言
第1部分 語 言 基 礎(chǔ)
第1章 逃逸分析/2
1.1 逃逸分析是什么/2
1.2 逃逸分析有什么作用/3
1.3 逃逸分析是怎么完成的/3
1.4 如何確定是否發(fā)生逃逸/4
1.5 Go與C/C 中的堆和棧是同一個(gè)概念嗎/5
第2章 延遲語句/6
2.1 延遲語句是什么/6
2.2 延遲語句的執(zhí)行順序是什么/7
2.3 如何拆解延遲語句/9
2.4 如何確定延遲語句的參數(shù)/10
2.5 閉包是什么/11
2.6 延遲語句如何配合恢復(fù)語句/11
2.7 defer鏈如何被遍歷執(zhí)行/13
2.8 為什么無法從父goroutine恢復(fù)子goroutine的panic/18
第3章 數(shù)據(jù)容器/20
3.1 數(shù)組與切片/20
3.1.1 數(shù)組和切片有何異同/20
3.1.2 切片如何被截。20
3.1.3 切片的容量是怎樣增長的/23
3.1.4 切片作為函數(shù)參數(shù)會(huì)被改變嗎/27
3.1.5 內(nèi)建函數(shù)make和new的區(qū)別是什么/28
3.2 散列表map/29
3.2.1 map 是什么/29
3.2.2 map 的底層實(shí)現(xiàn)原理是什么/30
3.2.3 map 中的 key 為什么是無序的/50
3.2.4 map 是線程安全的嗎/50
3.2.5 float類型可以作為map的key嗎/50
3.2.6 map 如何實(shí)現(xiàn)兩種 get 操作/52
3.2.7 如何比較兩個(gè) map 是否相等/53
3.2.8 可以對 map 的元素取地址嗎/54
3.2.9 可以邊遍歷邊刪除嗎/54
第4章 通道/55
4.1 CSP是什么/55
4.2 通道有哪些應(yīng)用/56
4.3 通道的底結(jié)構(gòu)/57
4.3.1 數(shù)據(jù)結(jié)構(gòu)/57
4.3.2 創(chuàng)建過程/58
4.3.3 接收過程/60
4.3.4 發(fā)送過程/67
4.3.5 收發(fā)數(shù)據(jù)的本質(zhì)/72
4.4 通道的關(guān)閉過程發(fā)生了什么/74
4.5 從一個(gè)關(guān)閉的通道里仍然能讀出數(shù)據(jù)嗎/75
4.6 如何優(yōu)雅地關(guān)閉通道/76
4.7 關(guān)于通道的happens-before有哪些/79
4.8 通道在什么情況下會(huì)引起資源泄漏/81
4.9 通道操作的情況總結(jié)/81
第5章 接口/82
5.1 Go接口與C 接口有何異同/82
5.2 Go語言與鴨子類型的關(guān)系/82
5.3 iface和eface的區(qū)別是什么/84
5.4 值接收者和指針接收者的區(qū)別/86
5.4.1 方法/86
5.4.2 值接收者和指針接收者/87
5.4.3 兩者分別在何時(shí)使用/89
5.5 如何用interface實(shí)現(xiàn)多態(tài)/89
5.6 接口的動(dòng)態(tài)類型和動(dòng)態(tài)值是什么/91
5.7 接口轉(zhuǎn)換的原理是什么/93
5.8 類型轉(zhuǎn)換和斷言的區(qū)別是什么/96
5.9 如何讓編譯器自動(dòng)檢測類型是否實(shí)現(xiàn)了接口/101
第2部分 語 言 類 庫
第6章 unsafe/104
6.1 如何利用unsafe包修改私有成員/104
6.2 如何利用unsafe獲取slice和map的長度/105
6.3 如何實(shí)現(xiàn)字符串和byte切片的零復(fù)制轉(zhuǎn)換/106
第7章 context/108
7.1 context是什么/108
7.2 context有什么作用/108
7.3 如何使用context/109
7.3.1 傳遞共享的數(shù)據(jù)/109
7.3.2 定時(shí)取消/111
7.3.3 防止 goroutine 泄漏/111
7.4 context底層原理是什么/112
7.4.1 接口/113
7.4.2 結(jié)構(gòu)體/114
第8章 錯(cuò)誤/124
8.1 接口error是什么/124
8.2 接口error有什么問題/125
8.3 如何理解關(guān)于error的三句諺語/126
8.3.1 視錯(cuò)誤為值/126
8.3.2 檢查并優(yōu)雅地處理錯(cuò)誤/128
8.3.3 只處理錯(cuò)誤一次/130
8.4 錯(cuò)誤處理的改進(jìn)/131
第9章 計(jì)時(shí)器/133
9.1 Timer底層數(shù)據(jù)結(jié)構(gòu)為什么用四叉堆而非二叉堆/133
9.2 Timer曾做過哪些重大的改進(jìn)/134
9.3 定時(shí)器的使用場景有哪些/134
9.4 Timer/Ticker 的計(jì)時(shí)功能有多準(zhǔn)確/134
9.5 定時(shí)器的實(shí)現(xiàn)還有其他哪些方式/137
第10章 反射/140
10.1 反射是什么/140
10.2 什么情況下需要使用反射/140
10.3 Go語言如何實(shí)現(xiàn)反射/140
10.3.1 types 和 interface/141
10.3.2 反射的基本函數(shù)/144
10.3.3 反射的三大定律/149
10.4 如何比較兩個(gè)對象是否完全相同/149
10.5 如何利用反射實(shí)現(xiàn)深度拷貝/151
第11章 同步模式/154
11.1 等待組 sync.WaitGroup 的原理是什么/154
11.2 緩存池 sync.Pool/157
11.2.1 如何使用sync.Pool/157
11.2.2 sync.Pool 是如何實(shí)現(xiàn)的/162
11.3 并發(fā)安全散列表 sync.Map/174
11.3.1 如何使用 sync.Map/175
11.3.2 sync.Map 底層如何實(shí)現(xiàn)/176
第3部分 高 級 特 性
第12章 調(diào)度機(jī)制/184
12.1 goroutine 和線程有什么區(qū)別/184
12.2 Go sheduler 是什么/184
12.3 goroutine 的調(diào)度時(shí)機(jī)有哪些/186
12.4 M:N模型是什么/187
12.5 工作竊取是什么/187
12.6 GPM底層數(shù)據(jù)結(jié)構(gòu)是怎樣的/188
12.7 scheduler 的初始化過程是怎樣的/193
12.8 主 goroutine 如何被創(chuàng)建/207
12.9 g0棧和用戶棧如何被切換/212
12.10 Go schedule循環(huán)如何啟動(dòng)/217
12.11 goroutine如何退出/221
12.12 schedule循環(huán)如何運(yùn)轉(zhuǎn)/226
12.13 M如何找工作/227
12.14 系統(tǒng)監(jiān)控sysmon后臺(tái)監(jiān)控線程做了什么/237
12.14.1 搶占進(jìn)行系統(tǒng)調(diào)用的P/240
12.14.2 搶占長時(shí)間運(yùn)行的P/243
12.15 異步搶占的原理是什么/247
第13章 內(nèi)存分配機(jī)制/252
13.1 管理內(nèi)存的動(dòng)機(jī)是什么,通常涉及哪些組件/252
13.1.1 內(nèi)存管理的動(dòng)機(jī)/252
13.1.2 內(nèi)存管理運(yùn)行時(shí)的組件/252
13.1.3 內(nèi)存的使用狀態(tài)/253
13.2 Go語言中的堆和棧概念與傳統(tǒng)意義上的堆和棧有什么區(qū)別/255
13.3 對象分配器是如何實(shí)現(xiàn)的/255
13.3.1 分配的基本策略/256
13.3.2 對象分配器的基本組件和層級/256
13.3.3 對象分配的產(chǎn)生條件和入口/259
13.3.4 大對象分配/261
13.3.5 小對象分配/262
13.3.6 微對象分配/264
13.4 頁分配器是如何實(shí)現(xiàn)的/265
13.4.1 頁的分配/265
13.4.2 跨度的分配/266
13.4.3 非托管對象與定長分配器/267
13.5 與內(nèi)存管理相關(guān)的運(yùn)行時(shí)組件還有哪些/269
13.5.1 執(zhí)行棧管理/269
13.5.2 垃圾回收器和拾荒器/271
13.6 衡量內(nèi)存消耗的指標(biāo)有哪些/272
13.7 運(yùn)行時(shí)內(nèi)存管理的演變歷程/278
13.7.1 演變過程/278
13.7.2 存在的問題/279
第14章 垃圾回收機(jī)制/280
14.1 垃圾回收的認(rèn)識/280
14.1.1 垃圾回收是什么,有什么作用/280
14.1.2 根對象到底是什么/280
14.1.3 常見的垃圾回收的實(shí)現(xiàn)方式有哪些,Go語言使用的是什么/281
14.1.4 三色標(biāo)記法是什么/281
14.1.5 STW是什么意思/282
14.1.6 如何觀察 Go 語言的垃圾回收現(xiàn)象/283
14.1.7 有了垃圾回收,為什么還會(huì)發(fā)生內(nèi)存泄漏/286
14.1.8 并發(fā)標(biāo)記清除法的難點(diǎn)是什么/288
14.1.9 什么是寫屏障、混合寫屏障,如何實(shí)現(xiàn)/289
14.2 垃圾回收機(jī)制的實(shí)現(xiàn)細(xì)節(jié)/291
14.2.1 Go語言中進(jìn)行垃圾回收的流程是什么/291
14.2.2 觸發(fā)垃圾回收的時(shí)機(jī)是什么/292
14.2.3 如果內(nèi)存分配速度超過了標(biāo)記清除的速度怎么辦/294
14.3 垃圾回收的優(yōu)化問題/295
14.3.1 垃圾回收關(guān)注的指標(biāo)有哪些/295
14.3.2 Go 的垃圾回收過程如何調(diào)優(yōu)/295
14.3.3 Go的垃圾回收有哪些相關(guān)的API,其作用分別是什么/305
14.4 歷史及演進(jìn)/305
14.4.1 Go 歷史各個(gè)版本在垃圾回收方面的改進(jìn)/305
14.4.2 Go在演化過程中還存在哪些其他設(shè)計(jì),為什么沒有被采用/307
14.4.3 Go語言中垃圾回收還存在哪些問題/307
結(jié)束語/310