什么是go程序执行的入口?

runtime包里面的函数(rt0_windows)

是程序员的运行编译环境 JVM就是java的runtime

初始化g0协程栈 是go代码的第一个协程

go启动新的协程调用的是底层的new proc 创建一个新的协程去执行runtime.main 放入调度器等待调度

初始化一个M,用来调度主协程

go的启动像是一个框架 go允许oo编程 但是缺乏继承结构 go的继承只是组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"
)

type User struct {
Id int64 `form:"id" binding:"required"`
Name string `form:"name"`
Address []string `form:"address"`
AddressMap map[string]string `form:"addressMap"`
}

type People struct {
User
}

func (u User) walk() {
fmt.Println("walk")
}

func main() {
p := People{}
// 匿名字段的语法糖
p.walk()

}

go的接口是隐式的(把接口的全部方法都实现就会接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
)

type alive interface {
walk()
}
type User struct {
Id int64 `form:"id" binding:"required"`
Name string `form:"name"`
Address []string `form:"address"`
AddressMap map[string]string `form:"addressMap"`
}

func (u User) walk() {
fmt.Println("walk")
}

go的高并发下的数据结构

空结构体

int的大小是跟随系统字长的

指针的大小也是跟随系统长度的

空结构体只有地址 没有长度(Zero Peace)为了节约内存 map channel

1
2
m := map[string]struct{}{}
m["a"] = struct{}{}

字符串

字符串本身是结构体 Data指针指向底层的Byte数组

遍历字符串

1
s = string([]rune(s)[:3])

map

go使用拉链法实现hashmap 每个桶中存储哈希的前八位

超出8个数据 就会存储到溢出桶里面

map的扩容

map的溢出桶太多时会导致严重的性能下降

runtime.mapassign()可能会触发扩容的情况 装载因子超过6.5

转载系数或者溢出桶的增加,会触发map的扩容 扩容并不是增加桶数,而是整理

map的并发问题

sync.Map使用了两个Map,分离了扩容的问题

不会引发扩容(查改)的操作使用read map

可能引发扩容(新增)的操作使用dirty map

GO隐式接口的特点

结构体实现接口 结构体指针实现接口
结构体初始化变量 通过 不通过
结构体指针初始化变量 通过 通过

go和java的区别:隐式接口,接口体实现接口,会帮着多实现一个结构体指针实现接口的方法

结构体实现接口,结构体和结构体指针初始化变量都是可以通过的;

结构体指针实现接口,结构体初始化变量不通过

空接口

空接口就是runtime.eface的结构体(数据类型 数据本身省略了很多信息)

nil和空接口 空结构体的区别和联系

nil 就是变量名 是空 有类型 是6种类型的“零值”

空结构体的指针不是nil 但是都相同是(zerobase)

空接口不一定是nil接口 只有两个属性都是nil才是nil接口