1.go特性
1.goroutine&线程
- 大小: 线程1~8MB 协程2KB
- 调度: 线程由操作系统调度 每个线程占用cpu的一段时间 线程的上下文切换涉及 用户态->内核态->用户态的转换、成本us级
协程由runtime调度 每个协程占用线程的一段时间 协程的上下文切换 只在用户态 成本ns级 - 阻塞: 线程被内核挂起让出cpu,涉及上下文切换
G在执行用户态调用时阻塞 G被runtime挂起,立刻运行下一个准备好了的协程
G在执行系统调用时阻塞 handoff: 见下图 - 调用: 系统调用: 例如读写文件、读写socket(go这里做的比较好阻塞时只有G被挂起)
用户态调用: 例如等channel可写/读 等锁 等条件变量⬑ 协程调度
2.内存逃逸
- 定义: 内存的生命周期变化 导致原本分配在栈上的内存逃逸到堆上需要GC 比如子函数返回内存的指针、内存被另外一个协程使用
- 如何查看逃逸
developer@srv-005:/data/src/phgames/api/rpc/knock/games/goldmaster $ go build -tags goldmaster -gcflags="-m" -o /dev/null # phgames/api/rpc/knock/games/goldmaster ./knock.go:29:9: &Tap{...} escapes to heap ./knock.go:45:20: make([]*protoKnock.GoldMasterSpin, 0) escapes to heap ./playerExt.go:33:9: &PlayerExt{...} escapes to heap - 编译参数
-gcflags="all=-N -l" //开发服使用 -N关闭优化 -l关闭内联
3.GC
- GC的触发条件: Runtime会维护一个NextGC变量(下次GC阈值) & 定时触发 & 业务代码调用runti.GC()生成环境一般不用会影响性能
- 黑灰白 白:不可达内存/或者更准确一点GC不知道它存在的内存(所有内存初始状态为白) 黑:可达内存 灰:等待变黑的内存(以及子指针待扫描)
- GC流程 stw: stop the world
Root Scan //stw扫描所有根对象 全局变量/栈/CPU寄存器 ↓ 并发Mark //1.顺藤摸瓜 把根对象里面的子对象加入到灰队列,自己变成黑 //2.写屏障:有如果有指针改变指向,把指针后面指向的内存加到灰队列里面去 //3.消费灰队列,自己变黑,子对象加入到灰队列 这是个递归循环 //4.灰队列消费差不多以后执行下一步 ↓ Mark Termination //消费灰队列 //这里要stw的原因 1.把可达的新内存加入到灰队列里面并不是一个原子操作 涉及寄存器硬件延时 会导致新内存还没被标识灰在下一步会被GC干掉 //2.业务协程可以疯狂的创建白色内存,需要stw停下来,最后扫描一次栈 ↓ Concurrent Sweep //标识为白的内存回收 - 如何减少GC压力
sync.Pool(对象池) 对象复用 减少临时对象 预分配slice strings.Builder(字符串拼接 扩容次数少)
4.http&websocket&socket
- http: 一问一答,服务器不能主动发消息给客户端,客户端想拿到服务器的主动消息 只能轮询。属于应用层
- websocket: 解决了http上面的痛点,支持服务器向客户端主动推送消息。属于应用层
- socket: 操作系统提供的底层接口 不属于4/7层协议
5.进程间通讯
- socket -> (tcp/udp)
- gRPC
包括:系列化/反序列化/连接池/http2多路复用(把多个消息分解成帧打包用一个tcp发送。无序)/消息头/粘包拆包/Keepalive/重连(部分场景)/超时
不包括:业务处理顺序/业务心跳/幂等/消息去重 - HTTP
- MQ
- redis
6.进程内通讯
- channel 环形队列+不可读/写阻塞协程且把协程加入到对应队列(sendq recvq)+可读/写状态从0->1唤醒+线程安全
不会扩容,初始化时容量就固定了 make(chan T, 64)
make(chan T) 无缓冲接收双方必须都到场 否则到场方阻塞
v, ok := <-ch ok=false为写已关闭 关闭时v为默认初始化值
向已关闭的channel写数据会panic 所以谁写谁关闭 多个协程写channel的时候 如果要关闭可以用屏障确保多个协程都不会再写channel才能关闭 - 共享内存+锁
- 条件变量
- atomic
- WaitGroup屏障
7.context
- 用于请求生命周期管理。客户端断开、超时、主动取消时,服务器可以通过case <-ctx.Done():或者ctx.Err()!=nil感知到。
主要应用于等io的时候,例如mysql查询,go服务器收到客户端取消以后可以回收mysql连接,mysql会停止查询 - ctx树结构,ctx2, cancel2 := context.WithCancel(ctx1) ctx2是ctx1的子节点,父节点取消任务时子节点会收到通知,子节点取消任务时父节点不会受到通知
- ctx可以携带traceId,在打印日志的时候把traceId打印出来可以知道是哪一次请求的日志,避免两次相同的请求打印的日志混淆
2.go语法
1.make&new
- new 分配对应类型的内存并返回对应类型的指针 new(map[string]int)只会分配一个指针的内存 指针指向map map并不会初始化
- make 创建并初始化 slice/map/channel
2.defer
- 在return表达式后执行
- 注册时压栈,执行时出栈
func test() (n int, err error) { defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) return fmt.Println("xx") } func main() { _, _ = test() }xx 3 2 1
3.sync.Once
文档更新时间: 2026-06-26 23:29 作者:morninglu
