语法
new和make的区别
- new分配内存(可能在栈上,可能在堆上)。内存里的值是对应类型的0值, 不能初始化指定。返回的是指针。
- make分配和初始化内存,只能用于切片、map和channel三个类型,第二个参数表示长度,除了map另外两种类型必须给值。返回的是原始类型。
slice append 扩容规则
cap是append后的大小
golang
newcap := old.cap
if newcap+newcap < cap {
newcap = cap
} else {
for {
if old.len < 1024 {
newcap += newcap
} else {
newcap += newcap / 4
}
if newcap >= cap {
break
}
}
}
进程
进程间通信(IPC)
模块化设计:将复杂系统拆分为多个独立进程(如 Web 服务器的 "主进程 + 工作进程" 架构);
资源共享:多个进程共享文件、数据库连接、硬件设备(如打印机);
负载均衡:将任务分发到多个进程并行处理(如分布式计算);
故障隔离:单个进程崩溃不影响整个系统(如微服务架构)。
- 管道:基于文件描述符的单向字节流通信机制,由操作系统在内核中创建一个 "缓冲区"(本质是环形队列),进程通过读写文件描述符来操作这个缓冲区。管道分为两种:匿名管道(仅适用于父子、兄弟)、命名管道
- 消息队列:消息队列是操作系统内核维护的 "消息链表",进程可向队列中添加消息(结构化数据),其他进程从队列中读取消息。每个消息包含 "类型标识" 和 "数据体",接收方可以按类型过滤消息。异步、多个进程可同时操作同一队列。
- 共享内存:多个进程共享同一块物理内存区域的通信方式,进程通过映射该内存区域到自身的虚拟地址空间,直接读写内存实现数据交换。核心优势是无需数据拷贝。共享内存本身不提供同步机制,需配合信号量(Semaphore)或互斥锁(Mutex)防止 "并发冲突"(如两个进程同时写同一块内存)。
- 信号量:信号量是用于进程间同步与互斥的计数器,本质是一个内核维护的整数,通过P()(减 1)和V()(加 1)两个原子操作实现进程协调。
- 信号:(紧急通知)信号是操作系统向进程发送的异步通知,用于告知进程发生了某个事件(如中断、异常、用户指令)。每个信号有唯一的编号(如 Linux 中SIGINT为 2,SIGKILL为 9),进程可通过注册 "信号处理函数" 来响应信号,或采用默认行为(如终止进程、忽略信号)。
- Socket套接字:基于网络协议的通信接口,支持 "本地进程间通信" 和 "跨主机网络通信",是最灵活、应用最广泛的 IPC 方式。服务端通过socket()创建套接字,bind()绑定 IP 和端口,listen()监听连接;
客户端通过socket()创建套接字,connect()连接服务端 IP 和端口;
服务端通过accept()接收连接,得到与客户端对应的新套接字;
双方通过read()/write()或recv()/send()读写数据,完成通信;
通信结束后,通过close()关闭套接字 - 远程过程调用RPC:像调用本地函数一样调用远程方法。基于网络的 "函数级通信" 机制,屏蔽了网络通信的底层细节,让开发者可以像调用本地函数一样调用远程进程的函数。
- 消息队列中间件MQ:是基于 "生产者 - 消费者" 模型的分布式 IPC 服务,本质是一个独立的进程(或集群),负责存储和转发消息。与内核级消息队列不同,MQ 是用户态的中间件,支持跨主机、高可靠、高并发的消息传输。
协程
GMP调度模型

- work stealing 机制:当本线程无可运行的 G 时,尝试从其他线程绑定的 P 偷取 G,而不是销毁线程。
- hand off 机制: 当本线程因为 G 进行系统调用阻塞时,线程释放绑定的 P,把 P 转移给其他空闲的线程执行。
内存
垃圾回收 gc
- 低延迟:STW 时间控制在亚毫秒级别
- 高吞吐:GC 开销占总 CPU 时间的比例尽可能小
- 可预测性:避免长时间的 GC 暂停
- 并发性:与用户程序并发执行
白色:未被访问的对象,可能是垃圾
灰色:已被访问但其引用的对象尚未全部访问
黑色:已被访问且其引用的对象也已全部访问 - 初始化:所有对象为白色,根对象标记为灰色
- 标记循环:从灰色对象集合中取出一个对象将其标记为黑色将其直接引用的白色对象标记为灰色
- 结束条件:灰色对象集合为空
- 清除:回收所有白色对象
强三色不变性:黑色对象不能直接指向白色对象
弱三色不变性:黑色对象可以指向白色对象,但必须存在从灰色对象到该白色对象的路径
完整流程:
- GC触发:当堆内存达到上次 GC 后的 2 倍时/默认两分钟触发一次/调用runtime.gc
- 标记准备(STW):停止所有用户的goroutine,启动写屏障,扫描栈和全局变量,将根对象标记为灰色, 启动标记worker
- 并发标记:标记 worker 并发处理灰色对象, 户程序继续执行,写屏障保证正确性, 当没有更多灰色对象时,进入标记终止阶段
- 标记终止(STW), 停止所有用户 goroutine 和标记 worker,完成最后的标记工作,关闭写屏障,计算下次 GC 的触发条件
- 并发清除:后台 goroutine 负责回收白色对象, 内存回收是惰性的,按需进行, 不影响用户程序的执行
如果赋值器试图创建一个从黑色对象指向白色对象的指针,写屏障就会介入。例如,当执行 objBlack.field = objWhite 这样的操作时,写屏障会确保 objWhite(或与其相关的对象)被标记为灰色,从而保证它不会被遗漏。
插入写屏障的核心思想是:当一个指针从对象 A 指向对象 B 时(A.ptr = B),如果对象 B 是白色的,那么写屏障会强制将对象 B 标记为灰色。
删除写屏障关注的是被覆盖的旧指针。当一个指针字段 A.ptr 原本指向对象 B,现在要改为指向对象 C(或者 nil)时(oldVal = A.ptr; A.ptr = C),删除写屏障会在赋值发生 之前 对 oldVal(即 B)进行处理。如果 oldVal 指向的对象是白色的,并且它可能因为这次引用断开而失去唯一的灰色或黑色先行者,那么删除写屏障会将 oldVal 指向的对象标记为灰色。B 是白色,且没有任何引用者被 GC 遍历到,最终被误回收 ------ 但如果 B 其实还有其他隐藏引用(比如栈上的临时变量),就会导致程序崩溃。