子曾经曰过: 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决
Golang Note
简介
-
golang是编译型语言,自带编译器,无须单独安装;
-
快速编译、高效执行、易于开发;
-
并发模型、内存分配、垃圾回收、静态链接、标准库、工具链;
-
包名应当以小写的单个单词来命名, 且不应使用下划线或者驼峰;
-
golang编译原理过程
词法和语法分析 -> 类型检查和AST转换 -> 通用SSA生成 -> 机器代码生成
数据类型
-
数组声明&初始化
var arr1 [5]int arr2 := [...]int{1, 2, 3} arr3 := [5]int{1:2, 3: 2} arr4 := [5]int{1, 2, 3, 4, 5}
-
数组长度是数组类型的一部分, 数组是不可变的,数组的复制属于深拷贝;
-
切片
1. 切片属于引用类型, 是对底层数组的一个引用; 2. 创建切片时, make函数会在底层创建一个数组,该数组切片在底层维护,程序猿不可见;
// 切片的结构 runtime.h struct Slice{ // must not move anything byte* array; // actual data uintgo len; // number of elements uintgo cap; // allocated number of elements };
// 切片的初始化方式 //1. 在现有数组的基础上引用 var arr = [...]int{1, 2, 3, 4, 5} slice := arr[1:3] //2. 通过make函数创建 slice1 := make([]int, 2, 3) /*底层会创建一个数组,被隐藏了,对外不可见*/ //3. 最简方式 slice2 := []int {1, 2, 3, 4}
// 遍历方式 //1. 普通遍历 for i := 0; i < len(slice); i++ { //逻辑代码 } //2. for ... range遍历 for k, v := range slice { //逻辑代码 }
-
append
// append(slice []Type, elems ...Type) 1. 向切片的尾部添加数据,返回新的slice对象; 2. elems可以是Type类型的数据,也可以是Type类型的切片; 3. 将一个切片追加到另一个切片中需要带上...; 4. 可能会引起扩容;
var1 := []string{"apple", "banana", "orange"} var2 := []string{"cat", "dog", "pig"} // 将var2追加到var1的末尾 var3 := append(var1, var2...) //切片元素删除 slice := append(slice[:i], slice[i+1]...)
扩容机制: 1. 当需要的容量超过原容量的2倍时,会使用需要的容量作为新容量; 2. 当原切片容量小于1024时,新切片容量直接翻倍; 3. 当原切片容量大于/等于1024时,会反复增加上一次容量的25%,直道新容量超过所需要的容量;
-
new & make
1. new用来分配内存,返回指针类型,不会初始化内存,只会将内存置零; 2. make仅限于slice, map, channel类型数据的内存分配, 并返回已初始化内存的类型的值; (slice,map,channel为引用类型,使用前必须初始化)
-
switch
leter := 'i' switch letter { case 'a', 'b', 'c', 'd': //TODO case 'f': //TODO default: //TODO } // 没有表达式的switch num := 30 switch { case num >= 0 && num < 50: //TODO }
-
切片不能用作map的键,因为它们的相等性还未定义;
-
映射也是引用类型;
函数&方法&接口
-
闭包&匿名函数
- 闭包是匿名函数与匿名函数所引用的环境的组合。
- 闭包是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
- 闭包可以通过匿名函数来构成。
-
方法
1. 是一类作用在某个数据类型上的函数; 2. 这类函数在函数签名中会有个接收器,来表明当前定义的函数会作用在该接收器上; 3. golang中除Interface类型外的任何其他数据类型都可以定义方法;
-
接口
1. 接口是一组仅包含方法名、参数和返回值,未具体实现的方法的集合; 2. 如果某个类型实现了一个接口的所有方法,则认为这个类型实现了该接口; 3. 接口的实现都是隐式的;
Why Interface? -泛型编程 -隐藏具体实现 -providing interception points
type TestInterface interface { TestFunc(str string) string } type TestStruct struct {} func (handle *TestStruct) TestFunc(str string) string { //TODO something } func Test(fun TestInterface) { //TODO something } func main () { var me TestStruct Test(me) }
-
defer
1. 在return返回之前执行; 2. 后进先出; 3. 被推迟函数的实参在推迟执行时就会求值,而不是在调用执行时才求值; 返回值=xxx 调用defer函数 空的return
goroutine & channel
-
信道类型:
//chan, chan<-, <-chan. <-是用来指定chan的方向的,没有给出就是双向 chreadandwrite :=make(chan int) //创建无缓冲channel chonlyread := make(<-chan int) //创建只读channel chonlywrite := make(chan<- int) //创建只写channel
-
如果没有缓冲,接收者和发送者需要同时就绪才会通信,否则调用者就会阻塞;
-
未初始化的chan是不可以通信的;
-
goroutine: csp并发模型, gpm调度模型
-
work-stealing:
runtime.schedule() { // only 1/61 of the time, check the global runnable queue for a G. // if not found, check the local queue. // if not found, // try to steal from other Ps. // if not, check the global runnable queue. // if not found, poll network. } Once a runnable G is found, it is executed until it is blocked.
内存管理
-
逃逸分析
如果函数外部没有引用,则优先放到栈中,如果申请的内存过大,就会放到堆中; 如果函数外部存在引用,则必定放到堆中; go build -gcflags '-m'
-
内存管理
1. Go在程序启动时,会向操作系统申请一大块内存,之后自行管理。 2. Go内存管理的基本单元是mspan,它由若干个页组成,每种mspan可以分配特定大小的object。 3. mcache, mcentral, mheap是Go内存管理的三大组件,层层递进。mcache管理线程在本地缓存的mspan;mcentral管理全局的mspan供所有线程使用;mheap管理Go的所有动态分配内存。 4. 极小对象会分配在一个object中,以节省资源,使用tiny分配器分配内存;一般小对象通过mspan分配内存;大对象则直接由mheap分配内存。
-
golang-gc:
stop the world (mark & sweep 算法) 改进:三色标记法 (写屏障机制: gc跑的过程中,可以监控对象的内存修改,并对对象进行重新标记,一次非常短暂的stw)