Golang 底层原理剖析笔记-数据结构

本文详细介绍了Golang中的基本数据结构,包括字符串的二进制安全、数组的静态存储、切片的动态共享、Map的哈希表实现以及Channel和Context在并发编程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构

字符串

// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
//
// Deprecated: Use unsafe.String or unsafe.StringData instead.
type StringHeader struct {
	Data uintptr
	Len  int
}
  • 不可变,不支持下标
  • golang 的字符串是二机制安全的。
  • 在和 byte 数组进行转换的时候如果 小于32B 则利用缓存,超过就会使用到堆空间
  • 在同 range 关键字配合使用的时候实际是转换为了 rune 数组来进行的(utf-8 也是占用4个字节)。

数组

// An Array represents an array type.
type Array struct {
	len  int64
	elem Type
}
  • 数组是静态类型的数据结构,会占用一块连续的内存区域,不能动态的改变大小。
  • 数组长度小于 4 的时候,运行时会被放入栈空间维护,大于 4 的时候位于内存的静态只读区域。
  • 数组作为参数传递的时候是按值传递的,也就是说数组会拷贝出一份来传入函数中。

切片

// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
//
// Deprecated: Use unsafe.Slice or unsafe.SliceData instead.
type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}
  • 切片是动态的类型数据,设计思想基于共享内存的方式来进行的。
  • 使用切片的时候要注意 append 时候可能导致切片地址变更,而截取操作会共享一个切片内存空间。
  • 切片扩容时如果大小小于 256 会进行翻倍,如果超过,每次增加 1.25 倍。
  • 在分配的时候 如果大小超过 64 KB 会直接在堆里进行分配。
  • 传参的过程中不会导致底层的数据全部复制。
  • 同数组的比较上来看,就是减少了复制次数、重用了内存空间、可以动态的改变大小。
  • cap函数可以利用在切片上查看容量
  • 切片如果使用范围查找,比如[:i+1] 时,会判断是否超过长度,不会产生溢出

Map

// A header for a Go map.
type hmap struct {
	// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
	// Make sure this stays in sync with the compiler's definition.
	count     int // # live cells == size of map.  Must be first (used by len() builtin)
	flags     uint8
	B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
	noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
	hash0     uint32 // hash seed

	buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
	oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
	nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

	extra *mapextra // optional fields
}
  • map 的结构由哈希表、哈希桶、溢出桶组成,是一个KV结构的数据。
  • 支持随机的读写。
  • 扩容的时候可能由于负载因子超过6.5(默认)或者溢出桶数据太多导致。每次扩容翻倍,采用写时复制的技术来减少不可用时间。
  • 解决冲突的策略是开放寻址法。
  • 配合range 的时候,每次遍历都是无序的(源码中进行了随机化)。
  • 根据hash 后 N 位进行哈希桶定位,N = log2(桶大小)
  • 并发不安全,设计者认为 map 主要用在局部操作,所以也没提供。
  • 扩容条件:
    • 超过负载因子,扩容 2 倍
    • 溢出桶数量超过 2^15 时,等量扩容(map 里元素总数少,但是 bucket 数量多,比如插入后删除了很多,所以重新开辟 bucket 空间,进行整理,减少内部碎片)

map 的寻址

https://www.jb51.net/article/273673.htm

在这里插入图片描述

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
}

type waitq struct {
	first *sudog
	last  *sudog
}
  • channel 是 golang 对 CSP(communicating sequential process,通信顺序进程) 的具体实现,即通过通道进行通信实现任务的同步。
  • channel 是 goalng 提供的同步工具的核心数据结构之一。是并发安全的。
  • 底层由数组构成的 buf 和 双向链表组成的读写等待队列组成。
  • 大小在一开始就会被确定(数组管理缓冲区域),无法动态修改。属于静态类型
  • 使用中,channel 可能是 nil、无缓冲、带缓冲三种情况。
    • nil 对读写都会阻塞
    • 无缓冲的和有缓冲的主要区别在无缓冲chnnel 如果在没有读等待的时候写会被阻塞
    • 有缓冲的只有在没有缓冲空间的时候才会写阻塞。
  • channel 同样提供了只读、只写、读写channel 类型。
  • close 后依然可以读取channel 的数据,如果关闭的channel 中没有数据了,会返回对应的数据的 0 值。
  • select 多个channel 的时候会对channel 进行排序,防止死锁
  • select 的时候,会对case的顺序用洗牌算法打乱,防止饥饿

Context

  • golang 的 context 的设计是作为调用链路的信息载体存在的。
  • Backgroud 会返回一个空的 ctx
  • WithCancel 返回一个带通道的 ctx,可以通过调用cancel 来向通道发送一个信号,需要监听 ctx->done channel。
  • WithTimeOut 超时的 ctx,同时会返回一个 cancel 函数,帮助显示的取消 context。子协程同样需要监听 ctx->done 这个channel。
  • WithValue 方法可以返回一个带 KV 的ctx,用来传递自定义的一些数据,比如 logID 等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值