golang
golang
init 和 mian 函数
- init 函数
- 同一个 package 可以定义多个 init 方法
- 同一个 package 不同文件 init 方法执行按照文件名先后顺序执行
- 同一个 go 文件中可以重复定义 init 方法
- 按定义顺序执行
- 按照 import 顺序调用其他包的 init 函数
- 导入顺序 mian -> A -> B -> C 则 init 执行的顺序正好相反
- init 函数都在一个 goroutine 内执行
- 执行完之后再执行 main 函数
- 同一个 package 可以定义多个 init 方法
byte 和 rune 有什么区别
- 都是字符类型, 都是别名类型
- byte 本质是 uint8 类型的别名, 代表了 ASCII 码的一个字符
- rune 本质是 int32 类型的别名, 代表了 UTF-8 字符
Go struct 能不能比较
- 如果 struct 有不能比较的字段, 就不能比较
- 只能比较是否相等, 不能比较大小
- 所有属性都相等并且顺序一致的 struct 才能比较
goroutine 和线程的区别
- 内存占用
- goroutine 栈内存消耗为 2KB
- Thread 消耗 1MB
- 创建和销毁
- Thread 创建和销毁都是操作系统内核级别的, 通常由线程池管理
- goroutine 由 Go runtime 负责创建, 创建销毁消耗非常小, 是用户级
- 切换
- Thread 切换时, 需要保存各种寄存器
- goroutine 切换只需要保存 3 个寄存器
PC 程序计数器
Stack Pointer 栈顶指针
BP 基址指针
slice 和数组的区别
- slice 的底层数据结构是数组
- 数组是定长的, slice 可以扩容
- 数组就是一片连续的内存, slice 实际上是一个结构体, 包含长度, 容量, 底层数组
1
2
3
4
5type slice struct{
array unsafe.Pointer // 元素指针
len int // 数组长度
cap int // 容量
} - 底层数据可以被多个 slice 指向, 所以对一个 slice 操作有可能影响其他 slice
map 实现原理
- 数据被放入一个由桶组成的有序数组中, 每个桶最多可以存放 8 个
key/value
对 - key 的 Hash 值低位用于在该数组中定位到桶, 高 8 位用于在桶中区分
key/value
对 - 超了会链接到额外的溢出桶
overflow uintptr
所以数据结构为- hash 数组
- 桶内 key-value 数组
- 溢出的桶链表
- 当 Hash 超过阈值需要扩容时, 会分配一个新的桶数组, 一般是旧的 2 倍
- go 不会一次全量拷贝, 耗时太大, 会在每次读写 map 的时候动态迁移
为什么会 Hash 冲突
- 通过哈希函数产生的哈希值是有限的, 数据可能比较多, 导致经过哈希函数处理后出现相同的哈希值
go map 并发导致 panic 如何解决
- go 中 map 不支持并发读写
- 使用
sync.map
go 的垃圾回收
- 无分代
- 不整理(回收过程中不对对象进行移动与整理)
- 并发(与用户代码并发执行)
- 三色标记清除法
golang
http://showyoubug.cn/2024/05/26/golang/