进程是操作系统对一个正在运行的程序的一种抽象,进程是资源分配的最小单位。
那么为什么有了进程,还要线程呢?
原因如下:
进程间的信息难以共享数据,父子进程并未共享内存,需要通过进程间通信(IPC),在进程间进行信息交换,性能开销较大。 创建进程(一般是调用 fork 方法)的性能开销较大。
协程(Coroutine)是用户态的线程。通常创建协程时,会从进程的堆中分配一段内存作为协程的栈。
线程的栈有 8 MB,而协程栈的大小通常只有 KB,而 Go 语言的协程更夸张,只有 2-4KB,非常的轻巧。
Go 语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。因为拷 贝的内容有时候是非引用类型(int、string、struct 等这些),这样就在函 数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan 等这 些),这样就可以修改原内容数据。
Golang 的引用类型包括 slice、map 和 channel。它们有复杂的内部结构,除 了申请内存外,还需要初始化相关属性。内置函数 new 计算类型大小,为其分 配零值内存,返回指针。而 make 会被编译器翻译成具体的创建函数,由其分 配内存和初始化成员结构,返回对象而非指针。
Golang 中 map 的底层实现是一个散列表,因此实现 map 的过程实际上就是实现 散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫 hmap(a header for a go map),一个叫 bmap(a bucket for a Go map,通常叫其 bucket)。
Go 语言中,不要通过共享内存来通信,而要通过通信来实现内存共享。Go 的 CSP(Communicating Sequential Process)并发模型,中文可以叫做通信顺序进 程,是通过 goroutine 和 channel 来实现的。
channel 收发遵循先进先出 FIFO 的原则。分为有缓冲区和无缓冲区,channel 中包括 buffer、sendx 和 recvx 收发的位置(ring buffer 记录实现)、sendq、 recv。当 channel 因为缓冲区不足而阻塞了队列,则使用双向链表存储。
给一个 nil channel 发送数据,造成永远阻塞 从一个 nil channel 接收数据,造成永远阻塞 给一个已经关闭的 channel 发送数据,引起 panic 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零 值 无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的 关闭一个 nil channel 将会发生 panic
channel 中使用了 ring buffer(环形缓冲区) 来缓存写入的数据。ring buffer 有很多好处,而且非常适合用来实现 FIFO 式的固定长度队列。
在 channel 中,ring buffer 是一个缓冲区为 8 的 channel buffer,recvx 指向最早被读取的数 据,sendx 指向再次写入时插入的位置。
切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对 底层数组的抽象。因为基于数组实现,所以它的底层的内存是连续分配的,效 率非常高,还可以通过索引获得数据。
切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用 底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一 个只读对象,其工作机制类似数组指针的一种封装。
切片对象非常小,是因为它是只有 3 个字段的数据结构:
指向底层数组的指针 切片的长度 切片的容量
Golang 在 1.8版本之后,GC策略框架已经奠定,就是并发三色标记法+混合写屏障机制
• 对象分为三种颜色标记:黑、灰、白
• 黑对象代表,对象自身存活,且其指向对象都已标记完成
• 灰对象代表,对象自身存活,但其指向对象还未标记完成
• 白对象代表,对象尙未被标记到,可能是垃圾对象
• 标记开始前,将根对象(全局对象、栈上局部变量等)置黑,将其所指向的对象置灰
• 标记规则是,从灰对象出发,将其所指向的对象都置灰. 所有指向对象都置灰后,当前灰对象置黑
• 标记结束后,白色对象就是不可达的垃圾对象,需要进行清扫.
gcTriggerHeap 分配对象时触发 堆已分配内存达到阈值 gcTriggerTime 由 forcegchelper 守护协程定时触发 每2分钟触发一次 gcTriggerCycle 用户调用 runtime.GC 方法 上一轮 GC 已结束
• 行锁 Record Lock: 锁定具体行记录,依附于索引存在
• 间隙锁 Gap Lock: 针对行记录之间的空隙加锁
• 临键锁 Next-Key Lock: 本质上是间隙锁加行锁形成的组合
• 意向锁 Intention Lock: 在具体操作行为前进行意向声明
行锁 Record Lock 锁定的对象是具体的行记录,行锁会分为共享和独占两种模式
这里有一点需要额外强调的是,行锁的施加需要依附于索引而存在,因此触发加锁行为的 SQL 语句是否走到索引就尤为关键,倘若检索条件未命中任何索引,那么锁的粒度就会由行锁上升为表锁.
此外,需要明白 innodb 中的表结构是强依赖于主键(一级索引)的,因此在通过非主键索引加行锁时,在锁住索引的同时也会针对行记录对应的主键加锁.
• 读未提交:存在脏读、不可重复读、幻读问题
• 读已提交:存在不可重复读、幻读问题
• 可重复读:通过 MVCC 规避上述问题(innodb默认采用)