之前也有文章講過go調(diào)用堆棧的話題,但并沒有完全講清楚,這里補充里面缺漏的幾個點。
阻塞
goroutine 1 [select, 2 minutes]:
方括號里的select表示阻塞原因,具體定義見runtime.waitReason 。
后面的時間是阻塞時間。需要注意的是這只是一個大概時間,是在gc的過程中標記的,所以如果這個goroutine不需要gc,那么永遠也不會有值。
PC偏移
github.com/robfig/cron.(*Cron).run(0xc0000c44b0)
cron.go:191 +0x28d
行號后面的16進制數(shù)是什么?pc偏移。是指函數(shù)入口(cron.(*Cron).run)到調(diào)用處(也就是行號指位置)的指令位置偏差。一般很少用到,除非下面這種特殊情況:
func main() {
rand.Seed(time.Now().UnixNano())
_, _, _ = foo(), foo(), foo()
}
func foo() int {
if rand.NormFloat64() > 0 {
panic("")
}
return 0
}
//main.main()
// main.go:10 +0x7b 或 +0x80 或 +0x85
函數(shù)參數(shù)
函數(shù)參數(shù)是最復雜的部分,牽涉到go的很多底層實現(xiàn)。
輸入?yún)?shù),輸出參數(shù)
為什么經(jīng)常只有一個參數(shù)的函數(shù)堆棧里卻跟著兩個數(shù)呢?另一個是輸出。
func main() {
rand.Seed(time.Now().UnixNano())
r := rand.Int() //2e78e7b163438cc2
fmt.Printf("%x\n", r)
foo(r)
}
func foo(i int) (o int) {
o = rand.Int() //36dd26e720cac1fe
fmt.Printf("%x\n", o)
defer panic("want to panic")
return
}
//main.foo(2e78e7b163438cc2, 36dd26e720cac1fe)
// main.go:21 +0xdd
結(jié)構(gòu)體展開
結(jié)構(gòu)體會被展開,然后比較短的字段會被打包成一個uint。
type S struct {
a int
b int
c int
d int
a1 bool
b1 byte
c1 bool
d1 byte
}
func main() {
foo(S{})
}
func foo(s S) {
panic("want to panic")
noInline()
}
func noInline() {
fmt.Sprint()
}
//main.foo(0x0, 0x0, 0x0, 0x0, 0x0)
// main.go:67 +0x39
但是這種打包是依賴字段順序的。
type S struct {
a int
a1 bool
b int
b1 bool
c int
c1 bool
d int
d1 bool
}
func main() {
foo(S{})
}
func foo(s S) {
panic("want to panic")
noInline()
}
func noInline() {
fmt.Sprint()
}
//main.foo(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
// main.go:67 +0x39
函數(shù)參數(shù)對照表
下面的表涵蓋了大部分情況
| 函數(shù)簽名 | 輸入 | 堆棧輸出 | 說明 |
|---|---|---|---|
| foo(float64) | 1 | (0x3ff0000000000000) | |
| foo(complex64) | 1+3i | (0x404000003f800000) | 兩個32位浮點打包為一個64位數(shù) |
| foo(complex128) | 1+3i | (0x3ff0000000000000, 0x4008000000000000) | |
| foo(string) | "中文" | (0x10adc82, 0x6) | (指針,字節(jié)數(shù)) |
| foo(interface{}) | "" | (0x108e520, 0x10bff30) | (類型指針,值指針) |
| foo(interface{}) | (*string)(nil) | (0x108b8e0, 0x0) | 值為nil,類型不為nil |
| foo(interface{}) | nil | (0x0, 0x0) | 值,類型都為nil |
| foo([]byte) | make([]byte,3,6) | (0xc000070f82, 0x3, 0x6) | (指針,len,cap) |
| foo(map[string]string) | make(map[string]string) | (0xc000070e48) | |
| foo(chan struct{}) | make(chan struct{}) | (0xc00005e060) | |
| foo([200]int) | [200]int{} | (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...) | 數(shù)組展開 |
| foo(func()) | nil | (0x0) | |
| foo(int16, uint16) | 1,2 | (0xc000020001) | 兩個16位數(shù)打包為32位數(shù),僅低32位有效 |
| foo(bool)bool | false | (0xc000070f00, 0xc00005c058) | 前一個數(shù)為輸入,僅低8位有效,后一個數(shù)為輸出,未初始化 |
| foo(struct {a,b int}) | struct {a,b int}{} | (0x0, 0x0) | 結(jié)構(gòu)體展開 |
| foo(struct {a int;b bool;c int;d bool;e byte}) | struct {a int;b bool;c byte;d bool;e byte}{1,true,2,false,3} | (0x1, 0x1, 0x2, 0x300) | d,e合并,b不合并 |
| foo(struct {a int;b bool;c string;d bool;e byte}) | struct {a int;b bool;c string;d bool;e byte}{1,true,"a",false,3} | (0x1, 0x1, 0x10adb18, 0x1, 0x300) | d,e合并 |
| foo(struct {a struct{a,b byte};b bool}) | struct {a struct{a,b byte};b bool}{struct{a,b byte}{3,4},true} | (0xc000010403) | 內(nèi)嵌結(jié)構(gòu)體合并 |
| foo(struct {a struct{a int;b byte};b bool}) | struct {a struct{a int;b byte};b bool}{struct {a int;b byte}{5 , 7 },false} | (0x5, 0xc000072f07, 0xc00005e000) | 內(nèi)嵌結(jié)構(gòu)體不合并 |