内容简介:之前也有方括号里的select表示阻塞原因,具体定义见后面的时间是阻塞时间。需要注意的是这只是一个大概时间,是在
之前也有 文章 讲过 go 调用堆栈的话题,但并没有完全讲清楚,这里补充里面缺漏的几个点。
阻塞
goroutine 1 [select, 2 minutes]:
方括号里的select表示阻塞原因,具体定义见 runtime.waitReason 。
后面的时间是阻塞时间。需要注意的是这只是一个大概时间,是在 gc的过程中标记的 ,所以如果这个goroutine不需要gc,那么永远也不会有值。
PC偏移
github.com/robfig/cron.(*Cron).run(0xc0000c44b0) cron.go:191 +0x28d
行号后面的16进制数是什么?pc偏移。是指函数入口(cron.(*Cron).run)到调用处(也就是行号指位置)的指令位置偏差。一般很少用到,除非下面这种特殊情况:
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
函数参数
函数参数是最复杂的部分,牵涉到go的很多底层实现。
输入参数,输出参数
为什么经常只有一个参数的函数堆栈里却跟着两个数呢?另一个是输出。
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
结构体展开
结构体会被展开,然后比较短的字段会被打包成一个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
函数参数对照表
下面的表涵盖了大部分情况
函数签名 | 输入 | 堆栈输出 | 说明 |
---|---|---|---|
foo(float64) | 1 | (0x3ff0000000000000) | |
foo(complex64) | 1+3i | (0x404000003f800000) | 两个32位浮点打包为一个64位数 |
foo(complex128) | 1+3i | (0x3ff0000000000000, 0x4008000000000000) | |
foo(string) | "中文" | (0x10adc82, 0x6) | (指针,字节数) |
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, ...) | 数组展开 |
foo(func()) | nil | (0x0) | |
foo(int16, uint16) | 1,2 | (0xc000020001) | 两个16位数打包为32位数,仅低32位有效 |
foo(bool)bool | false | (0xc000070f00, 0xc00005c058) | 前一个数为输入,仅低8位有效,后一个数为输出,未初始化 |
foo(struct {a,b int}) | struct {a,b int}{} | (0x0, 0x0) | 结构体展开 |
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) | 内嵌结构体合并 |
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) | 内嵌结构体不合并 |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 调用堆栈
- 前端进阶(第一期)-调用堆栈笔记
- GDB入门教程之查看函数调用堆栈
- JavaScript的工作原理:引擎,运行时和调用堆栈
- 【译】JavaScript的工作原理:引擎,运行时和调用堆栈的概述
- 用于在高性能计算应用中检测异常调用堆栈树的可视分析框架 (A Visual Analytics Framework for th...
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Writing Windows VxDs and Device Drivers, Second Edition
Karen Hazzah / CMP / 1996-01-12 / USD 54.95
Software developer and author Karen Hazzah expands her original treatise on device drivers in the second edition of "Writing Windows VxDs and Device Drivers." The book and companion disk include the a......一起来看看 《Writing Windows VxDs and Device Drivers, Second Edition》 这本书的介绍吧!