以下列出一些常見的 GoLang 面試問題以及根據作者經驗提供的相應答案。 此列表會經常更新,加入新的理解。 請繼續關注。
什麼是 GoLang 的 GMP 模型?
GoLang 使用 goroutine 來實現並發,並且以高並發支持而聞名,因為該語言定義了自己的 goroutine 調度和處理系統,該系統被稱為 GMP 模型。
它的工作原理是 M 通常被定義為正在產生的 OS 線程(然後每個 M 線程應與一個 Process 隊列相關聯,並且每個 Process 隊列可以有多個 goroutine。Process 隊列的數量由 GOMAXPROCS()
定義。當 M 處理完其關聯的 P 中的所有 G 時,它會從其他 M 竊取 G,也會從全域 P 隊列中竊取。
如果 M 由於 IO 阻塞或網路阻塞而掛起,它會將自己從 P 中分離出來,並且此 P 將與另一個可用的 M 相關聯,如果不存在,則會建立一個新的 M。
如果 goroutine 無法放入本地 P,它將被放入全域 P 隊列。 如果 M 無法從其他 M 竊取 G,它將從全域 P 隊列中獲取。
每個 goroutine 只有 2K 的堆疊大小,這非常小,這就是 GoLang 支持更多並發的原因。 並且這種 goroutine 執行之間的切換是基於使用者線程空間,因此切換上下文的成本與線程或進程相比不高
GoLang 中的 GC 機制是什麼?
有三個階段:Mark Setup、Mark 和 Mark Termination。 Mark Setup 和 Mark Terminations 是 STW。 Mark 階段是並發的。
在執行 Mark Setup 時,將開啟 Write Barrier 以確保資料完整性,然後它將等待所有正在運行的 goroutine 停止。
當 Marking 開始時,它將佔用 25% 的 CPU,並為自己分配一個 P 來進行收集。 標記階段將標記仍在使用的 head,它會嘗試查看所有 goroutine 的堆疊並遍歷其 root 指標,然後從 root 遍歷並進行標記。 如果標記無法及時完成,它將執行 Mark Assist,這意味著要求其他應用程式 goroutine 幫助。 加快標記速度。
在執行 Mark Termination 時,Write Barrier 將被關閉,並且將完成清理工作。
何時開始執行 GC?
- GC 百分比,即與先前 GC 週期中使用的記憶體相比,要分配的新記憶體的百分比。
- 通過調用 runtime.GC() 手動觸發
Slice 的底層工作原理是什麼?
Slice 描述的是陣列的一部分,它與陣列不同。
Slice header 具有長度、容量以及第零個元素指標。 底層由陣列支持。 如果達到容量,則無法再增長,需要重新建立底層陣列並重新分配。 如果未達到容量,則可以在需要時增長。
Panic、defer 和 recover
對於 defer,它的工作方式是 FILO
-
defer 語句被評估時,會評估 deferred 函數的參數。
-
在周圍函數返回後,以 Last In First Out 順序執行 Deferred 函數調用
-
Deferred 函數可以讀取和分配給返回函數的命名返回值。
Panic 將返回執行,並且函數中的 defers 將繼續執行。
Recover 僅在 deferred 函數內部有用。 在普通函數中,recover 沒有任何效果,也不會執行任何操作。
Defer recover 僅適用於當前 goroutine,它不適用於其產生的 goroutine。
Context,它非常強大
Context 介面中的 4 個函數:
- Deadline(),
- Done(),
- Err(),
- Value(key interface{})
這樣做的好處是資源共享、goroutine 控制
有一些派生的 goroutine,例如 WithCancel、WithDeadline、WithTimeout、WithValue
WithCancel:
-
Cancel 函數將級聯到子上下文
-
建立新的子上下文時,它將在父上下文中註冊。
WithValue
-
該鍵必須是可比較的,因為在調用 Value() 函數時存在 c.key == key 檢查
-
slice、map 和函數不可比較。 主要是因為 slice 不可比較,因為它是一種引用類型,其中僅比較地址沒有意義,並且由於 len/cap 功能,比較 slice 中的內容的複雜性也會增加
Channel
Channel 是一種通過通信共享記憶體的方式
有緩衝和非緩衝通道
- 關閉 nil 或已關閉的通道將會 panic
- 將值發送到 nil 通道會永遠阻塞,關閉的通道將會 panic
- 從 nil 通道接收資料會永遠阻塞,關閉的通道將立即返回
何時會發生 goroutine 洩漏?
一些情況包括:
- 當通道未關閉,而 goroutine 一直在等待從通道接收資料時
- 當某些資源未正確關閉,而某些 goroutine 仍在等待來自另一端的響應,並且沒有為阻塞 IO 設置足夠的超時時
new() 和 make() 之間有什麼區別?
new()
僅分配記憶體,而 make()
將初始化資料
使用 new()
建立的物件已準備好使用,但需要在需要時顯式初始化其屬性才能使用
make()
只能用於 slice、map 和 channel 類型。
詳細差異可以在這裡找到。
指標和非指標方法接收器之間的區別?
就像函數調用中的按引用傳遞與按值傳遞一樣。
如果要改變 struct,或者 struct 對於接收器來說太大,則會選擇定義指標方法接收器。
如果某些函數具有指標接收器,則其餘函數也應該具有指標接收器,以保持一致性。
如何檢查 struct 是否實現了介面?
在 GoLang 中沒有像 Java 等其他語言那樣的 implements 關鍵字。 如果 struct 實現了介面的所有方法,則認為它實現了該介面。 如果 struct 代碼與介面定義不在同一位置,則可能難以找出 struct 是否實現了特定的介面。 在某些情況下,這可能會導致運行時問題。
有幾種方法可以檢查:
- 使用虛擬物件進行類型斷言。
var _ = (SomeStruct{}).(SomeInterface)
- 使用反射。 reflect.TypeOf(OneInterface).Implements(reflect.TypeOf(SomeInterface).Elem())
對於其他方法,可以在這裡找到。