go 数据理解
go 数据理解
一:
1.基础数据结构
1.1slice切片,变量赋值,引用的数组可能是同一个,
package main
import "fmt"
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := slice[2:5]
s2 := s1[2:6:7]
s2 = append(s2, 100)
s2 = append(s2, 200)
s1[2] = 20
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(slice)
}
输出结果
[2 3 20]
[4 5 6 7 100 200]
[0 1 2 3 20 5 6 7 100 9]
底层数据结构: slice 结构体如何表示切片。数组,长度,容量
内存分配: 如何使用 mallocgc 为切片的底层数组分配内存,并考虑是否需要进行垃圾回收扫描。
错误处理: 在创建切片时如何进行长度和容量的范围检查,并触发 panic。
数据复制: 使用 memmove 和 slicecopy 等函数高效地复制切片数据。
切片扩容策略: growslice 和 nextslicecap 函数实现了当切片容量不足时,如何分配新的内存并确定新的容量。
与反射的交互: reflect_growslice 函数为反射包提供了扩容切片的功能。
性能优化: 针对不同元素大小进行了特殊的处理,例如大小为 1 和 2 的幂的情况。
内存分配:使用 mallocgc 进行内存分配,根据需要启用写屏障(write barrier)以确保垃圾收集的正确性。
容量扩展策略:采用指数增长策略,平衡内存使用和性能。
并发控制:在并发环境中,使用写屏障和内存读写范围检查(race detector、MSAN、ASAN)来确保内存操作的安全性。
1.2map
hmap 结构:
hmap 是 Go 中 map 的核心结构,包含了哈希表的主要信息,如桶的数量、哈希种子、桶数组等。
buckets 字段是一个指向桶数组的指针,每个桶可以存储最多 bucketCnt(通常是 8)个键值对。
oldbuckets 字段用于在哈希表扩容时存储旧的桶数组。
bmap 结构:
bmap 代表一个桶(bucket),包含 tophash 数组用于存储键的哈希值高位,以及键和值的实际存储位置。
桶中的键和值是交替存储的,以优化内存布局。
操作函数:
mapaccess1 和 mapaccess2:用于访问 map 中的值,mapaccess1 返回值的指针,mapaccess2 返回一个布尔值表示键是否存在。
mapassign:用于向 map 中插入或更新键值对。
mapdelete:用于从 map 中删除键值对。
mapiterinit 和 mapiternext:用于迭代 map 中的键值对。
内存管理:
makeBucketArray:用于分配桶数组,并处理扩容时的桶数组复制。
hashGrow:用于触发哈希表的扩容操作。
evacuate:用于在扩容时将旧桶中的键值对迁移到新桶中。
并发控制:
使用 hashWriting 标志位来确保对 map 的并发读写安全。
使用原子操作(如 atomic.Or8)来更新 hmap 的标志位。
性能优化:
通过预分配溢出桶来减少内存分配的次数。
使用 minTopHash 和 emptyRest 等标志位来优化查找和迭代操作
1.3 channel
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
发生 panic 的情况有三种:向一个关闭的 channel 进行写操作;关闭一个 nil 的 channel;重复关闭一个 channel
读、写一个 nil channel 都会被阻塞