1.go特性

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



2.go语法

1.make&new
  1. new 分配对应类型的内存并返回对应类型的指针 new(map[string]int)只会分配一个指针的内存 指针指向map map并不会初始化
  2. make 创建并初始化 slice/map/channel
2.defer
  1. 在return表达式后执行
  2. 注册时压栈,执行时出栈
    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