一,golang語言的數(shù)組與切片的區(qū)別
答:數(shù)組是一個固定長度的基本數(shù)據(jù)結(jié)構(gòu)類型,slice是基于數(shù)組實(shí)現(xiàn)的一種長度可變的數(shù)據(jù)結(jié)構(gòu)常用類型
具體區(qū)別如下:
1,創(chuàng)建方式不同,數(shù)組如abc := [...]int{1, 2, 3, 4},slice如abc:=[]int{1, 2, 3,4} 或者 abc:=make([]int, 4, 10)
2,傳遞方式不同,數(shù)組是值傳遞,slice是引用傳遞
3,底層結(jié)構(gòu)不同,數(shù)組基本數(shù)據(jù)類型,slice header的底層結(jié)構(gòu)如下
type SliceHeader struct {
? ? Data uintptr
? ? Len? int
? ? Cap? int
}
其中SliceHeader是slice的運(yùn)行時表示形式
參考:https://golang.org/ref/spec#Array_types
https://blog.golang.org/slices-intro
https://golang.org/pkg/reflect/#SliceHeader
二,golang的map類型與C++的map類型區(qū)別
1, golang的map類型,僅僅是一個哈希表,具體結(jié)構(gòu)如下:
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count? ? int // # live cells == size of map.? Must be first (used by len() builtin)
flags? ? uint8
B? ? ? ? uint8? // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0? ? uint32 // hash seed
buckets? ? unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate? uintptr? ? ? ? // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
參考:https://golang.org/src/runtime/map.go
其中特性如下:
申請必須make,或者使用:=方式,直接使用未初始化的map會panic;
引用類型;
key的類型必須是可比較類型;
并發(fā)使用不安全,需要加sync.RWMutex鎖;
不指定迭代順序,若需要順序,則需要另外申請slice保存keys,然后sort.Ints(keys);
參考:https://blog.golang.org/maps
2,C++的map的底層結(jié)構(gòu)是紅黑樹
三,close通道關(guān)閉之后,Println輸出之后的值,是否可以寫入
1,close通道關(guān)閉之后,執(zhí)行fmt.Println(<- ch),若ch還有值,則輸出ch的值,否則輸出ch類型的默認(rèn)值
2,若寫入close的通道,則會panic,報錯:panic: send on closed channel
四,簡述chan類型
1,貫穿go語言的設(shè)計(jì)思想:不要通過共享內(nèi)存來通信,而應(yīng)該通過通信來共享內(nèi)存。
2,類似一個先進(jìn)先出隊(duì)列;其中buf是一個循環(huán)鏈表結(jié)構(gòu);lock是互斥鎖,所以線程安全;
3,分為無緩沖通道和有緩存通道,無緩沖通道必須先有消費(fèi)協(xié)程,然后再可發(fā)送數(shù)據(jù);
4,關(guān)閉chan,使用close,關(guān)閉之后不可再寫入,但可以消費(fèi)chan中的數(shù)據(jù)。
五,defer/panic/recover/return的理解
1,defer延遲執(zhí)行
2.1 在函數(shù)return之前可以執(zhí)行的一段代碼,常用于資源釋放,解鎖,增加recover等操作。
2.2 多個defer執(zhí)行順序LIFO
例子:
package main
import (
"fmt"
"errors"
)
func main() {
? fmt.Printf("with named param, x: %d\n", namedParam())
? fmt.Printf("error from err1: %v\n", err1())
}
func namedParam() (x int) {
? x = 1
? defer func() { x = 2 }()
? return x
}
func err1() error {
? var err error
? defer func() {
? ? ? if r := recover(); r != nil {
? ? ? ? err = errors.New("recovered")
? ? ? }
? }()
? panic(`foo`)
? return err
}
輸出:
with named param, x: 2
error from err1: <nil>
2,panic恐慌
2.1 內(nèi)置函數(shù),出現(xiàn)致命錯誤可以使用panic退出程序,如:panic("has error")
3,recover恢復(fù)
3.1 內(nèi)置函數(shù),重新獲得panicking協(xié)程的控制,防止程序崩潰。注意像fatal的錯誤無法捕獲,如map的并發(fā)讀寫:fatal error: concurrent map read and map write;死鎖:fatal error: all goroutines are asleep - deadlock(申請無緩沖chan,直接生產(chǎn)數(shù)據(jù)會報此致命錯誤)
舉例如下:
package main
import (
"fmt"
)
func main() {
defer fmt.Println("recover first ", recover())
defer func() {
if r := recover(); r != nil {
fmt.Println("recover ", r)
}
}()
defer fmt.Println("recover second ", recover())
panic("has error")
}
輸出:
recover second <nil>
recover? has error
recover first? <nil>
4,return函數(shù)返回
4.1 return操作不是原子操作,過程具體可以分為三步:
首先賦值,把return的值賦給返回值
其次檢查defer操作,有則調(diào)用,所以defer里面的函數(shù)是可以更改返回值的
最后返回返回值
六,簡述內(nèi)存逃逸,遇沒有遇見過內(nèi)存溢出及內(nèi)存泄漏
1,內(nèi)存逃逸
1.1 golang的內(nèi)存分配逃逸于棧和堆
1.2 查看逃逸分析日志命令:go build -gcflags=-m
1.3 逃逸場景:指針逃逸,返回局部變量指針;大對象逃逸,申請局部變量是大對象的時候;動態(tài)類型,申請不確定大小內(nèi)存的局部變量逃逸;閉包引用對象逃逸;
1.4 分析內(nèi)存逃逸好處:可減少gc的壓力,不逃逸則函數(shù)結(jié)束可以回收清理,不需要gc標(biāo)識清理過程(所以指針傳遞不一定比值傳遞效率高);棧上分配內(nèi)存效率更高(不是絕對,棧也會擴(kuò)容縮容);靜態(tài)分析,編譯時完成(不影響性能);
1.5 舉個例子如下:
package main
import "fmt"
func fibo() func() int {
a, b := 0, 1
fmt.Println("first a, b", a, b)
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := fibo()
for i := 0; i < 6; i++ {
fmt.Printf("fib: %d\n", f())
}
f1 := fibo()
fmt.Printf("fib1: %d\n", f1())
fmt.Println("f:%p", &f, ", f1:%p", &f1)
}
運(yùn)行輸出:
first a, b 0 1
fib: 1
fib: 1
fib: 2
fib: 3
fib: 5
fib: 8
first a, b 0 1
fib1: 1
f:%p 0xc00000e028 , f1:%p 0xc00000e038
另外可以分析逃逸情況,輸入命令:go build -gcflags=-m
輸出如下:
# test/t_memory_escape
./main.go:9:13: inlining call to fmt.Println
./main.go:11:9: can inline fibo.func1
./main.go:27:13: inlining call to fmt.Printf
./main.go:33:12: inlining call to fmt.Printf
./main.go:35:13: inlining call to fmt.Println
./main.go:7:2: moved to heap: a
./main.go:7:5: moved to heap: b
./main.go:9:14: "first a, b" escapes to heap
./main.go:9:14: a escapes to heap
./main.go:9:14: b escapes to heap
可以看見a/b倆變量都逃逸了
2,內(nèi)存溢出
2.1 golang的內(nèi)存溢出基本沒有見過,因?yàn)樗亩褩P畔⑹强梢詳U(kuò)容的,初始棧的容量是2k。
3,內(nèi)存泄漏
3.1 golang的內(nèi)存管理是使用gc機(jī)制自動化管理,使用方法是標(biāo)記清理法,具體到三色標(biāo)記法,另外使用屏障等技術(shù)提高回收效率。
3.2 golang是存在內(nèi)存泄漏的,如文件/套接字等句柄沒有關(guān)閉釋放。這個沒有釋放的句柄會堆積在內(nèi)存,gc并不會回收他們,導(dǎo)致內(nèi)存泄漏。也有可能是啟動的goroutine沒有按預(yù)期退出,導(dǎo)致協(xié)程泄漏。
七,有沒有使用過pprof/Benchmark工具
1,pprof工具
1.1 pprof工具是golang自帶的性能分析神器
1.2 查看堆棧信息:go tool pprof http://localhost:6060/debug/pprof/heap
1.3 查看cpu信息:go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
1.4 查看goroutine信息:go tool pprof http://localhost:6060/debug/pprof/goroutine
1.5 查看trace路徑信息:go tool pprof http://localhost:6060/debug/pprof/trace
2,Benchmark工具
1.1 性能測試工具
1.2 基準(zhǔn)測試,需要_test.go結(jié)尾
八,簡述golang語言的GMP機(jī)制
1,G是goroutine的縮寫,用來表示協(xié)程;M是machine的縮寫,用來表示線程,可以設(shè)置最大數(shù)量:SetMaxThreads;P是processor的縮寫,用來表示邏輯處理器,p的個數(shù)配置GOMAXPROCS;
2,p隊(duì)列有兩種,一種全局隊(duì)列,平衡多個p隊(duì)列之間的任務(wù),有鎖;一種本地隊(duì)列,無數(shù)據(jù)競爭,速度快,無鎖;
3,啟動方式,首先確定p的個數(shù),若有設(shè)置環(huán)境變量$GOMAXPROCS或者是runtime.GOMAXPROCS()則按設(shè)置數(shù)量,否則使用默認(rèn)值cpu核心數(shù)。其次動態(tài)調(diào)整m的數(shù)量,若沒有足夠的數(shù)量的m關(guān)聯(lián)p中可運(yùn)行的g的時候會新創(chuàng)m,如所有的m被阻塞時。
4,調(diào)度設(shè)計(jì)策略,首先當(dāng)m無g可運(yùn)行時,會優(yōu)先從其他p本地隊(duì)列盜取g來運(yùn)行,本地隊(duì)列沒有,才會盜取全局隊(duì)列;其次當(dāng)本地m運(yùn)行的g,因進(jìn)行系統(tǒng)調(diào)用而阻塞時,m會釋放綁定的p,p會轉(zhuǎn)移到其他空閑的m上運(yùn)行其他的g;最后go1.14及之后,屬于基于信號的搶占式調(diào)度,一個g不能無限占用cpu時間,占用一定時間是可以被搶占的,防止其他g餓死;
參考:https://learnku.com/articles/41728
九,簡述golang語言的gc機(jī)制
1,golang的gc(garbage collection)機(jī)制,使用方法是標(biāo)記清理法,具體到三色標(biāo)記法,另外使用屏障等技術(shù)提高回收效率。
十,簡述golang語言cgo原理
1,cgo主要是用來創(chuàng)建go語言包調(diào)用c代碼,詳細(xì)可見:https://golang.org/pkg/cmd/cgo/