Golang 内存管理探微——如何高效使用 Golang 内存以及腾讯云实战 杨晖

QCon大会

2019/06/25 发布于 技术 分类

QCon  QCon2019 

文字内容
1. Go语言内存探微 Go语言内存管理细节以及实践中内存使用的一些技巧 杨晖 腾讯云教育产业中心
3. 自我介绍 • Shanks(杨晖),人在广东漂泊十二年,打过工,创过业 • 腾讯云教育产业中心研发负责人 • 技术栈:后台开发/Go语言全家桶 • 爱好:巴萨、微信读书、跑步(4次全马,人生目标百马)
4. Why Golang? Language Ecosystem • 语法简洁 • 编译速度快 • Docker • ETCD • 开发高效 • 性能好 • Kubernetes • Influxdb • 原生支持并发 • GRPC • Beego Environment Stack • 有个好爸爸 • 云时代的C语言 • 云计算时代 • C/C++ • 社区活跃 Go语言
5. 目录结构 内存分配 内存分布 数据类型 内存结构 静态 编译器 处理 垃圾回收 动态 编程实践
6. 什么是虚拟内存 • 计算机系统内存管理的一种技术 • 每个进程都拥有独立的、连续的、统一的虚拟地址空间 • 通过MMU和物理内存映射,高效使用物理内存
7. 64位linux进程内存分布情况 • 理论上有16E的寻址空间,目前没有操作系统 会用到这么大的空间 • 目前用了48位的寻址空间,总的虚拟地址空间 为256TB • 用户空间为128T • 用户空间布局和32位linux布局一样
8. 堆和栈 开发者视角: 1、空间大小:堆>>栈 2、分配速度:栈>>堆 3、分配方式:栈(系统分配),堆(手动分配)
9. 一个简单的Go程序内存布局 func main() { for { time.Sleep(1) } }
10. Go语言类型的内存结构
11. 基础类型 类型 长度(字节) bool uint8, int8 默认值 说明 false 1 0 0 ~255, -128 ~ 127 0 uint8 0 0~65535, -32768 ~ 32767 uint32, int32 0 0 ~ 42亿,-21亿 ~ 21亿 float32 0.0 提供小数点后6位的精度 rune 0 Unicode Code Point, int32 uint64, int64 0 byte uint16, int16 float64 2 4 8 complex64 0 float64可以提供小数点后15位的精度 (0,0) complex128 16 (0,0) int, uint 4或者8 0 32位或者64位
12. 字符串 type _string struct { str *byte len int } • 类型为byte的只读切片([]byte, 长度为len) • 结构体大小:8(byte*) + 8(len)=16字节 • str指针指向字符串首字节,以字节为单位存储
13. 字符串长度 str := "qcon广州" fmt.Println(len(str)) fmt.Println(utf8.RuneCountInString(str)) for index, runeValue := range str { fmt.Printf("%d: %c\n", index, runeValue) } 结果: 10 6 0: q 1: c 2: o 3: n 4: 广 7: 州
14. 字符串连接 由于字符串是只读的,所以字符串连接操作必然会涉及到内存拷贝! 方式 用法 +操作符 s += "qcon" + "广州" + "2019" fmt.Sprintf strings.Join bytes.Buffer strings.Builder s = fmt.Sprint("qcon", "广州" , 2019) a := []string{"qcon", "广州", "2019"} s = strings.Join(a,"") var b bytes.Buffer b.WriteString("qcon") b.WriteString("广州") b.WriteString("2019") s = b.String() var b strings.Builder b.WriteString("qcon") b.WriteString("广州") b.WriteString("2019") s = b.String() 特点 每次拼接,都申请新的内存块 只能是字符串类型使用 可读性强 性能一般 内部使用[]byte实现 涉及到类型转换,可以拼接其他类型 性能一般 只能拼接字符串数组 不灵活 拼接字符串、字符和unicode 底层使用[]byte 涉及到string和[]byte之间转换 适用场景 少量字符串拼接时 少量非字符串类型拼接 时 已存在字符串数组时 少量字符串拼接时 拼接字符串、字符和unicode 使用unsafe.Pointer优化了string和[]byte之间 大量字符串拼接 的转换
15. 切片-slice type slice struct { array *byte len uint cap uint } • 切片为:数组的引用 • 结构体长度:8+8+8=24字节
16. 增加切片元素:append方法 cap不够时,cap < 1024, cap容量成倍增加;cap >= 1024时,按照1.25倍扩容 a := []int{1} fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: a = append(a, 2) fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: a = append(a, 3) fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: a = append(a, 4, fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: %d cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) %d cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) %d cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) 5) %d cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) 结果: len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 1 cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:1 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1] len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 2 cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:2 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2] len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 3 cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:4 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2 3] len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 5 cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:'>cap:8 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2 3 4 5]
17. 底层数组扩容时,运行时会新生成一段扩容后大小的内存, 然后把数据拷贝过去。这里涉及到一定的内存拷贝开销
18. 切片作为函数参数传递 func main() { a := []int{1, 2, 3} fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: %d cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) appendSlice(a) fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: %d cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) } func appendSlice(a []int) { a = append(a, 4) } 结果: len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 3 cap:'>cap:3 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2 3] len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 3 cap:'>cap:3 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2 3] 4去哪儿了?
19. 记住:切片是结构体,传入函数会新生成结构体实参。 如果需要在函数内部改变切片值,需要显式返回: func appendSlice(a []int) []int { a = append(a, 4) return a } 另外,还可以用指针传递
20. 指针传参 func main() { a := []int{1, 2, 3} fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: %d cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) appendSlice(&a) fmt.Printf("len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: %d cap:'>cap:%d data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:%+v\n", len(a), cap(a), a) } func appendSlice(a *[]int) { *a = append(*a, 4) } 结果: len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 3 cap:'>cap:3 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2 3] len:'>len:'>len:'>len:'>len:'>len:'>len:'>len: 4 cap:'>cap:6 data:'>data:'>data:'>data:'>data:'>data:'>data:'>data:[1 2 3 4]
21. Go语言中的指针 简约版C指针 • 类型安全:不支持指针运算 • 灵活性 • 参数传递 • 固定大小:8字节
22. Map • 结构体 • 底层使用桶来存储散列值 • 使用hash算法选择具体的桶 • 2倍速度扩容 type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Pointer nevacuate uintptr extra *mapextra }
23. interface • 结构体 • 方法集合,duck- type • 隐藏实现
24. interface type Stringer interface { String() string } type Binary uint64 func (i Binary) String() string { return strconv.FormatUint(i.Get(), 2) } func (i Binary) Get() uint64 { return uint64(i) } func main() { var b Binary = 200 s := Stringer(b) fmt.Print(s.String()) }
25. chan • 结构体 • 存储goroutine之间发送的消息和状态 type hchan struct { qcount uint dataqsiz uint buf unsafe.Pointer elemsize uint16 closed uint32 elemtype *_type sendx uint recvx uint recvq waitq sendq waitq lock mutex } c := make(chan int, 5)
26. Go语言编译器与内存 和C语言编译器一样,Go语言编译器也将Go代码转换为符合Linux进程内存规范的二 进制代码: • 执行代码加载到Text段 • 全局变量加载到Data段 • 临时变量和函数执行都会通过栈的Push和Pop来执行 • 堆上分配程序运行时申请的内存
27. 逃逸分析 • 编译期间确定一个对象放栈上还是放堆上 • 编译器如果能在编译期间确定变量生命周期,就会在栈上分配,否则就是逃逸行为, 需要在堆上分配内存 • 需要尽量避免逃逸行为
28. 逃逸的几种典型情况(非官方) • 函数返回内部变量的指针 • 发送指针或带有指针的值到 channel 中 • 在一个切片上存储指针或带指针的值 • slice 的背后数组被重新分配了,因为 append 时可能会超出其容量(cap) • 在 interface 类型上调用方法
29. 逃逸分析 检查是否逃逸:go build -gcflags "-m -m" type S struct{} func main() { var x S _ = *ref(x) } func ref(z S) *S { return &z }
30. Go语言内存管理
31. Golang Runtime • 线程调度 • 内存分配 • 垃圾回收 • 管道 • 接口 • 字典 • 切片
32. 一个优秀的内存分配器应该具备的素质 使用分配算法来合理在堆上分配内存,需要具备以下三个素质: • 分配速度快 • 节约空间 • 碎片少
33. 内存分配器历史 Tcmalloc,2007 Google出品 Ptmalloc, 1997 dlmalloc,1996 线程私有缓存,小对象使用 多线程支持 中心堆-线程堆模型 Glib2.3之后,成为linux默认内存分配 小对象无需加锁 器 对象大小分class,不同大小对象使用 最佳适配算法 不同class的span分配,减少碎片,加 相邻空闲块合并 快分配速度 双向链表、数结构 单一锁,不支持多线程
34. Go语言的做法 • 内存分配算法:使用TCMalloc算法原理 • 使用:new,make
35. TCMalloc分配流程 TCmalloc:thread-caching malloc GO进程申请大块虚拟内 存,自己管理 申请>32k对象 使用全局堆内存mheap分 配 内存不足,求助 申请<=32k对象 使用线程缓存mcache分配 没有空闲资源, 向mcentral申请新的mspan, 并切分为mcache需要大小 没有空闲资源, 向mheap申请新的mspan 使用中心缓存mcentral分 配 操作系统
36. 申请内存的两种方式 new //new的实现, _type是编译器进行生成的 func newobject(typ *_type) unsafe.Pointer { return mallocgc(typ.size, typ, true) } //new调用的函数, 除了new以外, make关键字编译出来的对应函 数也会调用这个函数 func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { ... } make //runtime/slice.go //make一个slice, 编译器生成以下方法. func makeslice(et *_type, len, cap int) slice { maxElements := maxSliceCap(et.size) if len < 0 uintptr(len) > maxElements { panicmakeslicelen() } if cap < len uintptr(cap) > maxElements { panicmakeslicecap() } p := mallocgc(et.size*uintptr(cap), et, true) return slice{p, len, cap} } 底层都是调用mallocgc,说明new 和 make出来的对象,都是在堆上生成,参与垃圾回收
37. New 和 Make 等价于: T *t = (T*)malloc(sizeof(T)) memset(t, 0, sizeof(T)) 返回指针,且值为零 Make只用来创建slice,map和channel 已初始化、返回为引用
38. New 和 Make s1 := new([]int) fmt.Println(s1) fmt.Printf("%p\n", s1) s2 := make([]int, 0) fmt.Println(s2) fmt.Printf("%p\n", &s2) 结果: &[] 0xc42000a060 [] 0xc42000a0a0
39. unsafe.Pointer 和 uintptr unsafe.Pointer • Unsafe.pointer 约等于C语言中的 (void *) uintptr • unsafe.Pointer 可以和 uintptr 进行相互转换 • 任何类型的指针值都可以和 pointer相互转换 • 用于指针运算 • 不可以参与指针运算 • GC 不把 uintptr 当指针,uintptr 无法持有对象 使用场景: • 相同内存结构变量相互转换 • 操作结构体内部变量
40. unsafe.Pointer 和 uintptr type Foo struct { A int B int } func main() { foo := &Foo{1, 2} fmt.Println(foo) base := uintptr(unsafe.Pointer(foo)) offset := unsafe.Offsetof(foo.A) ptr := unsafe.Pointer(base + offset) *(*int)(ptr) = 3 } fmt.Println(foo) 结果: &{1 2} &{3 2}
41. Go 语言垃圾回收 • STW • 查看内存消耗以及垃圾回收情况 • Sync.Pool
42. 垃圾回收算法优化历史 版本 发布时间 v1.0 2012/3 调度GM模型, GC STW 秒 v1.1 2013/5 调度G-P-M模型 同上 2013/12 调度实现合作的抢占式调度 同上 v1.3 2014/6 GC实现Mark STW, Sweep 并行.栈扩容方式由split stack改为复制方式的 continus stack. 添加sync.Pool 几百ms v1.4 2014/12 Runtime移除C代码; 实现准确式GC. 引入写屏障, 为1.5的并发GC做准备. 同上 v1.5 2015/8 Runtime完全移除C代码, 实现了Go的自举. GC 三色标记法, GOMAXPROCS默认为CPU核数, go tool trace引入 10-40ms级别 v1.6 2016/2 1.5中一些与并发GC不协调的地方更改. 集中式的GC协调协程, 改为状态机 实现. 全面支持HTTP/2 5-20ms级别 v1.7 2016/8 GC时栈收缩改为并发, 引入dense bitmap, 编译器性能改进, 正式引入 Context包, SSA引入 ms级 v1.8 2017/2 hybrid write barrier, 消除re-scanning stack, GC进入sub ms. Sort包改 进. HTTP graceful shutdown. Plugin支持 sub ms(18GB堆) V1.9 2017/8 类型别名, 增加sync.Map, 增加math/bits包等, 并行编译 基本同上 V1.10 2018/2 构建时使用缓存, pprof增加Web UI, 标准库增加strings.Builder 基本同上 V1.11 2018/8 go module, WebAssembly 基本同上 V1.12 2019/2 go module机制优化, binary-only package最后的支持, Runtime更为积极的 归还内存, GC流程代码清理 v1.2 改进特征 GC STW时间 Sub ms, 但几乎减少一半 注: GC STW时间与堆大小, 机器性能, 应用分配偏好, 对象数量均有关, 这里的数值仅供参考
43. Go 语言垃圾回收基础算法 Golang使用的gc方法是三色标记算法,是一种标记清扫算法基础上的改进算法 • 白色节点表示未被标记的对象 • 灰色节点表示已被标记存活且存在儿子节点未被扫描标记的对象 • 黑色节点表示已被标记存活且全部儿子节点已被标记的对象 • 所有节点依其状态做标注,当灰色节点数量为空时,白色节点回收,黑色节点全部置 为白色,等待新一轮gc启动
45. sync.Pool • 保存和复用临时对象,以减少内存分配,降低CG压力 • 临时对象还是会被GC,池中没对象,会使用new func生成一个新的对象
46. sync.Pool var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 1024) return &b }, } func main() { a := time.Now().Unix() for i := 0; i < 1000000000; i++{ obj := make([]byte,1024) _ = obj } b := time.Now().Unix() for i := 0; i < 1000000000; i++{ obj := bytePool.Get().(*[]byte) _ = obj bytePool.Put(obj) } c := time.Now().Unix() fmt.Println("without pool ", b - a, "s") fmt.Println("with pool ", c - b, "s") } 结果: without pool 23 s with pool 17 s
47. 内存相关实战
48. 相关工具 pprof 生成memory profile和cpu profile数据: • runtime/pprof:采集程序(非 Server)的运行数据进行分析 • net/http/pprof:采集 HTTP Server 的运行时数据进行分析 • github.com/pkg/profile(推荐): 可自定义控制profile的生命周期,有更全面的 mutexProfile、blockprofile等更多维度的分析工具。 查看GC • gctrace • gcvis
49. 例子一:定位hashIt函数内存问题 一个批量求hash的代码,运行发现性能不好(根因是频繁malloc),内存也比较大,需要优化,这里着重从内存分析。 //示例代码: package main import ( "hash/fnv" "github.com/pkg/profile" ) var in []byte func hashIt() uint64 { h := fnv.New64a() h.Write(in) out := h.Sum64() return out } func mainfunc() { in = []byte("aaaaaaaaaaaaaaa") for i := 0; i < 2000; i++ { in = append(in, 'a') hashIt() } } func main() { mainfunc() } //嵌入profile工具 func main() { defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop() mainfunc() } 执行: [root@localhost testPprof]# go build [root@localhost testPprof]# ./testPprof 2019/05/24 16:38:24 profile:'>profile: memory profiling enabled (rate 1), /tmp/profile566525644/mem.pprof 2019/05/24 16:38:24 profile:'>profile: memory profiling disabled, /tmp/profile566525644/mem.pprof [root@localhost testPprof]# go tool pprof -http=0.0.0.0:8080 /tmp/profile566525644/mem.pprof xdg-open: no method available for opening 'http://0.0.0.0:8080'
50. 例子一:定位hashIt函数内存问题 查看memory profile,并结合逃逸分析,发现 有大内存逃逸的问题 [root@localhost testPprof]# go build -gcflags '-m' # tencentcloud-sdk-gomaster/examples/soe/v20180724/testPprof ./test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17: inlining call to fnv.New64a ./test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17: hash.Hash64(&fnv.s?2) escapes to heap ./test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17: &fnv.s?2 escapes to heap ./test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17:'>test.go:11:17: moved to heap: fnv.s?2 ./test.go:18:13: ([]byte)("aaaaaaaaaaaaaaa") escapes to heap ./test.go:26:21: main ... argument does not escape :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: .this :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: .this :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: .this :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: hash.b :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: .this :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: .this :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: io.p :1:0: leaking param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param:'>param: .this
51. 例子一:定位hashIt函数内存问题 Memprof 优化点1:优化fnv.New64a函数,返回interface类型,优化内存60%(15KB) var in []byte var h hash.Hash64 func hashIt() uint64 { h.Write(in) out := h.Sum64() return out } func mainfunc() { in = []byte("aaaaaaaaaaaaaaa") h = fnv.New64a() for i := 0; i < 2000; i++ { in = append(in, 'a') hashIt() h.Reset() } } hashIt占用内存被优化
52. 例子一:定位hashIt函数内存问题 Memprof 优化点2:使用引用型的内置类型且append多份拷贝,进一步优化内存70%(5KB) var in [2000]byte var h hash.Hash64 func hashIt(i int) uint64 { h.Write(in[:i]) out := h.Sum64() return out } func mainfunc() { h = fnv.New64a() for i := 0; i < 2000; i++ { in[i] = 'a' hashIt(i) h.Reset() } }
53. 例子二:内存泄漏监控 生产环境看,GO本身的GC产生大量的变量逃逸比较多,正常使用下较少见到有内存泄 漏的问题。 可能产生内存泄露的两个场景: • cgo模块内存泄漏 • 资源句柄未正常管理导致资源未释放
54. 例子二:内存泄漏监控 使用pprof定位资源句柄泄漏 var b byte func httpGet() { resp, err := http.Get("http://www.qq.com/?a=" + string(b)) //something happen, func need to return.leads to defer not run. if err != nil true judgement { return } defer resp.Body.Close() } func mainfunc() { for i := 0; i < 100; i++ { //inspite of connection reuse r := rand.New(rand.NewSource(time.Now().UnixNano())).Int() b = byte(r%26 + 97) httpGet() } }
55. 例子二:内存泄漏监控 强制GC,发现dialConn仍未未释放深红色 func mainfunc() { for i := 0; i < 100; i++ { //inspite of connection reuse r := rand.New(rand.NewSource(time.Now().UnixNano())).Int() b = byte(r%26 + 97) httpGet() } runtime.GC() }
56. 例子二:内存泄漏监控 发现是judgement影响了defer的执行,修改judgement代码保证defer语句覆盖。 var b byte func httpGet() { resp, err := http.Get("http://www.qq.com/?a=" + string(b)) //something happen, func need to return.leads to defer not run. if err != nil { return } defer resp.Body.Close() if true judgement { return } }
57. 查看GC运行情况 - gctrace GODEBUG=gctrace=1 /path/to/binary
58. 查看GC运行情况 - gcvis
59. 查看GC运行情况-gcvis
60. 总结 • 内存检查作为必选项,内存出问题无小事 • 有垃圾回收,内存管理无忧,但了解Go语言内置类型的原理,有利于写出更好的代码 • 高性能场景下,优化一点是一点(cgo,pool,unsafe包)