go 程序怎么跑起来的
使用go build -x 观察编译连接过程
1go build -x hello.go
可执行文件
Linux 的可执行文件 ELF(Executable and Linkable Format) 为例,
ELF 由几部分构成:
- ELF header
- Section header
- Sections
操作系统执行可执行文件的步骤
解析 ELF header,加载文件到内存,从entry point 开始执行代码
readelf 配合dlv 找到程序入口
通过readelf -H中的entry找到程序⼊⼝
1[ubuntu@us_inner ~ 14:31:07]$readelf -h ./hello
2ELF Header:
3 Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
4 Class: ELF64
5 Data: 2's complement, little endian
6 Version: 1 (current)
7 OS/ABI: UNIX - System V
8 ABI Version: 0
9 Type: EXEC (Executable file)
10 Machine: Advanced Micro Devices X86-64
11 Version: 0x1
12 Entry point address: 0x45c020 (入口地址)
13 Start of program headers: 64 (bytes into file)
14 Start of section headers: 456 (bytes into file)
15 Flags: 0x0
16 Size of this header: 64 (bytes)
17 Size of program headers: 56 (bytes)
18 Number of program headers: 7
19 Size of section headers: 64 (bytes)
20 Number of section headers: 23
21 Section header string table index: 3
在dlv调试器中b *entry_addr找到代码位置
1sudo /usr/local/go/bin/go get github.com/go-delve/delve/cmd/dlv
2sudo /usr/local/go/bin/go install github.com/go-delve/delve/cmd/dlv
3
4
5[ubuntu@us_inner ~ 14:39:41]$/home/ubuntu/go/bin/dlv exec ./hello
6Type 'help' for list of commands.
7(dlv) b *0x45c020
8Breakpoint 1 set at 0x45c020 for _rt0_amd64_linux() /usr/local/go/src/runtime/rt0_linux_amd64.s:8
go中的四座大山
Runtime构成
Scheduler、Netpoll、内存管理、垃圾回收 等,把这几块啃了,那么go就很简单了。
go调度器
https://www.figma.com/proto/gByIPDf4nRr6No4dNYjn3e/bootstrap?page-id=242%3A7&nodeid=242%3A215&viewport=516%2C209%2C0.07501539587974548&scaling=scale-down-width
队列
P的本地runnext字段-> P的local run queue -> global run queue,多级队列减少锁竞争。
用户新创建的一定赋给runnext,重试到成功为止
调度循环:
线程M在持有P的情况下不断消费运⾏队列中的G的过程。
sysmon
⼀个后台⾼优先级循环,执⾏时不需要绑定任何的P
负责:
- 检查是否已经没有活动线程,如果是,则崩溃
- 轮询netpoll
- 剥离在syscall上阻塞的M的P
- 发信号,抢占已经执⾏时间过⻓的G
处理阻塞
dlv 代码调试
todo
go语法背后到秘密
1GOSSAFUNC=funcname go builld x.go
2# GOSSAFUNC=main go builld x.go
编译过程 https://godbolt.org
1go tool compile -S ./hello.go | grep "hello.go:5"
2
3# 该命令会生成.o的目标文件,并把目标的汇编内容输出
编译反编译工具
编译工具
1go tool compile -S ./hello.go | grep "hello.go:5"
反编译工具
看在runtime 执行了哪些函数
1go tool objdump ./hello
sp: 指向当前函数栈顶
p 本地队列 最多256个
性能调优
提前压测
锁临界区有慢操作,锁力度问题
同步改异步
json库,请求的cpu被优化了
map中含有指针,会给gc造成压力
调大gogc
逃逸分析
1[ubuntu@us01 ~ 13:09:46]$sudo `which go` build -gcflags="-m -l" main.go
2# command-line-arguments
3./main.go:4:6: can inline main
4./main.go:5:14: make([]int, 10240) escapes to heap
5[ubuntu@us01 ~ 13:10:02]$cat main.go
6package main
7
8func main(){
9 var s1 =make([]int,10240)
10 println(s1[0])
11}
-m 打印出逃逸分析信息,-l 表示禁止内联(更好的观察逃逸)
benchmark
https://www.cnblogs.com/jiujuan/p/14604609.html
io如何模拟
- time.Sleep
- 不要做mock,直接压测更好
pprof
线上一定要开启
压测看goroutine数,线程数,gc频率,goroutne在干嘛的
cpu,内存
cpu使用太高
- 应用逻辑导致
- json 序列化
- 使用优化的json库代替标准库
- 使用二进制编码代替json编码
- 干掉序列化,采用共享内存ipc通信
- md5算hash成本太高,使用cityhash,murmurhash
- json 序列化
- gc使用cpu过高
- 减少堆上分配
- sync.Pool进行堆对象重用
- map->slice
- 指针-> 非指针对象
- 多个小对象->合并一个大对象
- offheap
- 降低gc频率
- 修改GOGC
- make全局大slice
- 减少堆上分配
- 调度相关函数使用cpu过高
- 尝试使用goutine pool减少goroutine的创建和销毁
- 控制最大goroutine数量
内存使用过高
- 堆内存使用过多
- sync.Pool对象复用
- offheap
- goroutine栈占用过多内存
- 减少goroutine数量
- goroutine pool
- 减少goroutine数量
语言外优化
- 拆进程
- 水平扩容
go编译器
1# go tool compile 命令编译 unsafe.go 件,会得ࡠ一个扩名为 .o 的目标文件
2go tool compile unsafe.go
3# ls -l unsafe.a
垃圾回收
1package main
2import (
3 "fmt"
4 "os"
5 "runtime"
6 "runtime/trace"
7 "time"
8)
9func printStats(mem runtime.MemStats) {
10 runtime.ReadMemStats(&mem)
11 fmt.Println("mem.Alloc:", mem.Alloc)
12 fmt.Println("mem.TotalAlloc:", mem.TotalAlloc)
13 fmt.Println("mem.HeapAlloc:", mem.HeapAlloc)
14 fmt.Println("mem.NumGC:", mem.NumGC)
15 fmt.Println("-----")
16}
17
18func main(){
19 f, err := os.Create("/tmp/traceFile.out")
20 if err != nil {
21 panic(err)
22 }
23 defer f.Close()
24 err = trace.Start(f)
25 if err != nil {
26 fmt.Println(err)
27 return
28 }
29 defer trace.Stop()
30 var mem runtime.MemStats
31 printStats(mem)
32 for i := 0; i < 10; i++ {
33 s := make([]byte, 50000000)
34 if s == nil {
35 fmt.Println("Operation failed!")
36 }
37 }
38 printStats(mem)
39}
1GODEBUG=gctrace=1 go run xxx.go