golang高级讲义

  • 7 浏览
文字内容
1. Golang ⾼高级讲义 xiaorui.cc github.com/rfyiamcool v 0.2
2. menu slice、map runtime.scheduler sync lock memory sync map gc sync pool channel 逃逸分析
3. slice ptr len=2 cap=4 s1 := arr[1:3] arr := [5]int{10, 20, 30, 40, 50} 10 20 30 40 50 • ptr • • ptr len=3 s2 := arr[2:] cap=3 len • • 底层数组 占⽤用个数 cap • 容量量⼤大⼩小
4. slice • 函数值传递只需24字节 (array地址 + len + cap) • append • append会改变array的数据 • 当cap不不⾜足时,会重新make⼀一个新slice替换 ptr • 函数传slice指针可解决ptr变更更问题 • slice 线程不不安全
5. map
6. bucket map hash table hash1 hash2 … bucket key1 … key8 value1 … value8 key1 … value8 overflow bucket key1 … hash x value8 bucket key1 … key8 value1
7. map • map 可理理解为引⽤用 • map 内存释放问题 • go 1.10 value指针和值的性能 • map 线程不不安全 • • sync.map segment lock map
8. scheduler
9. syscall User Mode User Application C Library function open() write() system call interface Kernel Mode SYSTEM
10. syscall • • how trap syscall when syscall ?
11. what PMG G1 goroutine m1 thread P1 runQ
12. scheduler G1 m0 G2 P1 more P G G G m1 P2 G p link m only at the same time G p count affect parallel G
13. ext runtime struct 全局M列列表 runtime.allm 存放所有M的列列表 全局P列列表 runtime.allp 存放所有P的列列表 全局G列列表 runtime.allg 存放所有G的列列表 调度器器空闲M列列表 runtime.sched.midle 存放空闲M的列列表 调度器器空闲P列列表 runtime.sched.pidle 存放空闲P的列列表 调度器器可运⾏行行G队列列 runtime.shced.runq 存放可运⾏行行的G队列列 P可运⾏行行的G队列列 runq 存放当前P中可运⾏行行G … … …
14. when to scheduler channel garbage collection blocking syscall like file or network IO time.Sleep more
15. P status • Pgcstop • • Pidle • • 跟M关联 Psyscall • • 没有跟M关联 Prunning • • 正在进⾏行行垃圾回收 系统调⽤用 Pdead • 减少 GOMAXPROCS
16. M status • ⾃自旋中(spinning) • • 执⾏行行go代码中 • • M正在执⾏行行go代码, 这时候M会拥有⼀一个P 执⾏行行原⽣生代码中 • • M正在从运⾏行行队列列获取G, 这时候M会拥有⼀一个P M正在执⾏行行原⽣生代码或者阻塞的syscall, 这时M并不不拥有P 休眠中 • M发现⽆无待运⾏行行的G时会进⼊入休眠, 并添加到空闲M链表中, 这时M并不不拥有P
17. Goroutine status
18. goroutine视⻆角 go, 产⽣生新的g, 放到本地队列列或全局队列列 gopark, g置为waiting状态, 等待显示goready唤醒, 在poller中⽤用得较多 goready, g置为runnable状态, 放⼊入全局队列列 gosched, g显示调⽤用runtime.Gosched让出资源, 置为runnable状态, 放 ⼊入全局队列列 goexit, g执⾏行行完退出, g所属m切换到g0栈, 重新进⼊入schedule g陷⼊入syscall: net io和部分file io, 没有事件则gopark; 普通的阻塞系统调⽤用返回时,m重新进⼊入schedule
19. work stealing go func() G G G G G G G G G G G G find G G P P P M M M
20. go work stealing 1. pop from local runq 2. pop from global runq 3. pop from netpoller 4. pop from other p’ runq
21. 抢占 sysmon if block ? block • P syscall G M G G M • prunning • pmg long time psyscall • syscall long time P G G M
22. sysmon 1. netpoll(获取fd事件) 多功能 sysmon ! 循环调⽤用存在于整个⽣生命周期 min = 20us; max = 10ms 2. retake(抢占) 3. forcegc(按时间强制执⾏gc) 4. scavenge heap(释放⾃由列表中多余的项 减少内存占⽤) 5. more
23. unlink m & g case G1 m0 P 1. 2. 3. 4. network io syscall unlink m & g m pop from runq m run g context Epoll Event G G G network io G1
24. unlink p&m case G1 m0 P 1. 2. 3. 4. disk io syscall ulink p & m wakep or newM link p & m disk io G syscall m1 G G G1
25. memory
26. memory • 基于tcmalloc构建的多级内存池 • mcache: per-P cache,可以认为是 local cache。 • mcentral: 全局 cache,mcache 不不够⽤用的时候向 mcentral 申请。 • mheap: 当 mcentral 也不不够⽤用的时候,通过 mheap 向操作系统申请。 • mspan: 内存分配的基本单位
27. memory • mspan 默认 8k 起 • spans 512M • bitmap 16G • arena 512G
28. Mheap mspan memory next prev startAddr sizeclass freelist free[128] 系统虚拟地址空间 spans bitmap … arena_start spans bitmap arena arena_used arena_end mcache; 跟P数⽬目相等 central[67] tiny tinyoffset lock sizeclass … local_nsmallfree mcentral 全局67个 local_nlargefree alloc[67]
29. mspan memory alloc = [67]sizeclass 每⾏行行的sizeclass包含 ⼀一个mspan链表 tiny tinyoffset alloc[67] mcache // class bytes/obj bytes/span … // 2 16 8192 // 3 32 8192 if malloc 30 byte ; // 4 48 8192 // 5 64 8192 … // 23 448 8192 … // 43 4096 8192 … // 64 27264 81920 // 65 28672 57344 // 66 32768 32768 next prev sizeclass startAddr freelist next prev sizeclass startAddr freelist … 4k 4k
30. alloc memory rule • 如果object size>32KB, 则直接使⽤用mheap来分配空间; • 如果object size<16Byte, 则通过mcache的tiny分配器器来分配; • 如果16Byte < object size < 32KB,⾸首先尝试通过sizeclass对应的分配器器分 配; • 如果mcache没有空闲的span, 则向mcentral申请空闲块; • 如果mcentral也没空闲块,则向mheap申请并进⾏行行切分; • 如果mheap也没合适的span,则向系统申请新的内存空间。
31. object relase rule • 如果object size>32KB, 直接将span返还给mheap的⾃自由链; • 如果object size<32KB, 查找object对应sizeclass, 归还到mcache⾃自由 链; • 如果mcache⾃自由链过⻓长或内存过⼤大,将部分span归还到mcentral; • 如果某个范围的mspan都已经归还到mcentral,则将这部分mspan归还 到mheap⻚页堆; • ⽽而mheap会定时将释放的内存归还到系统;
32. gc
33. desc gc Golang 采⽤用了了标记清除的GC算法 Golang的标记清除是⼀一个三⾊色标记法的实现,对于三⾊色标记法,"三⾊色"的概 念可以简单的理理解为: ⽩白⾊色:还没有搜索过的对象(⽩白⾊色对象会被当成垃圾对象) 灰⾊色:正在搜索的对象 ⿊黑⾊色:搜索完成的对象(不不会当成垃圾对象,不不会被GC)
34. when trigger gc • gcTriggerHeap • 当前分配的内存达到⼀一定值就触发GC • • • gcTriggerTime • 当⼀一定时间没有执⾏行行过GC就触发GC • two minutes gcTriggerCycle • • default GCGO=100 要求启动新⼀一轮的GC, 已启动则跳过, ⼿手动触发GC的runtime.GC()会使⽤用这个条件 gcTriggerAlways • 强制触发GC
35. gc process 1. ⾸首先创建三个集合:⽩白、灰、⿊黑。 2. 将所有对象放⼊入⽩白⾊色集合中。 3. 然后从根节点开始遍历所有对象,把可追溯的对象从⽩白⾊色集合放⼊入灰⾊色集合。 4. 之后遍历灰⾊色集合,将灰⾊色对象引⽤用的对象从⽩白⾊色集合放⼊入灰⾊色集合,之后将此灰 ⾊色对象放⼊入⿊黑⾊色集合。 5. 重复4直到灰⾊色中⽆无任何对象。 6. 通过写屏障检测对象有变化,重复以上操作。 7. 回收所有⽩白⾊色对象
36. GC 扫描标记
37. GC 清除
38. GC 重置
39. forcegc & return os Sysmon 会强制两分钟进⾏行行⼀一次 GC 每5分钟会释放⼀一些span, 归还操作系统
40. map的释放情况 ⼤大量量的使⽤用big map下会出现对象被清楚,但释放不不⼲干净 ! map = nil debug.FreeOSMemory()
41. channel的释放情况 Channel被清空后,⼤大约10分钟释放内存 .
42. debug.FreeOSMemory() 元素被gc清除后,什什么时候释放内存归还 OS ? 编码说 5分钟, 但事实不不是这样… ⼿手动触发 FreeOSMemory() 会更更好的释放 !
43. debug GODEBUG=gctrace=1 func printMem() { runtime.ReadMemStats(&mem) log.Println("mem.Sys: ", mem.Sys/1024/1024) log.Println("mem.Alloc: ", mem.Alloc/1024/1024) log.Println("mem.TotalAlloc: ", mem.TotalAlloc/1024/ log.Println("mem.HeapAlloc: ", mem.HeapAlloc/1024 log.Println("mem.HeapSys: ", mem.HeapSys/1024/1 log.Println("mem.HeapObjects: ", mem.HeapObjects }
44. keyword 写屏障 ? 并发GC ? heap 内存什什么时候归还系统? golang1.9 gc的改进 ?
45. stack
46. stack
47. 逃逸分析 Stack Heap
48. 逃逸分析 产⽣生引⽤用, 直接堆逃逸
49. 逃逸分析 fmt引起的对象堆逃逸...
50. 逃逸分析 所以; 不不要肯定的认为 某个 对象在heap or stack上 . • • 分析汇编 • go tool compile -S xxx.go 对象分布分析 • go tool compile -m xxx.go
51. netpoller func (fd *FD) Read() --> func (pd *pollDesc) wait --> func poll_runtime_pollWait --> func netpollblock() 阻塞中 …
52. netpoller sysmon 唤醒中 …
53. timer.After timer.NewTimer go timer timer.Tick
54. go timer 小顶堆
55. sync.pool obj obj obj P obj GC obj obj P obj obj
56. sync.pool • • • 使⽤用slice做缓冲对象存储 mutex 保护 shared slice shared会被偷⾛走, private 留留个本
57. sync.map
58. sync.map
59. sync.Mutex sema waiting queue G1 G G G G2 • • • cas 适当减少syscall 通过runtime调度唤醒 G
60. sync.Mutex sema waiting queue G1 G G G G G2 • atomic.CompareAndSwap • 适当减少syscall • 通过runtime调度唤醒
61. • • 双检查锁机制 使⽤用互斥量量保护wait queue
62. sync.Mutex Mutex也不不廉价
63. sync.Mutex • sync.Mutex 锁性能不不是问题 • noDeferLock 轻易易⼲干到 5000w/s • DeferLock 也可以到 1700w/s • 锁竞争才是最⼤大问题 open 10个Goroutine
64. 思考 ? • • • 为什什么已经有cas指令,为什什么还需要⽤用futex锁 ? golang 底层都谁在调⽤用futex锁 ? 是否可以适当规避futex的使⽤用 ?
65. channel设计 ch := make(chan Task, 3) Hchan alloc an hchan on the heap circular queue 2 buf send index sendx 1 receive index recvx 0 mutex wait send q lock wait recv q 1 0 sudog Heap sendq recvq closed g g1 elem … task
66. send full channel? G1 G1 ch <- task, but chan is full G1 waiting is block, so gopark() calls into the scheduler M set G1 to waiting runQ P unlink G1, M schedule a runnable G from the P runqueue G2 runnable
67. resuming goroutines G2 G2 task := <- chan, then sender can run goready(G1) current G calls into the scheduler M set G1 to runnable runQ P puts it on runqueue return to G2 G1 runnable G0
68. direct write G4 Stack stack G3 Stack Heap G3直接把数据copy给G4 sudog⾥里里elem地址!
69. tools go tool pprof go tool trace go-torch go test -bench=. -benchmem …
71. “Q & A” –rfyiamcool