使用指针的优势

  • 节省内存,提高性能(减少数据复制带来的开销)
  • 能够直接修改变量的值
  • 用于构建链表和树等数据结构
  • 优化内存管理管理(将指针设为nil,使对象可被垃圾回收)

值类型和引用类型

  • 值类型直接储存值,内存通常在栈上分配
  • 引用类型储存的是一个地址,内存通常在堆上分配,通过GC回收

指针和引用的区别

严格来说go没有引用类型,例如map 底层是一个指向 hmap 的指针,这就可以解释即使函数传参是按值传递,由于传递的是指针的拷贝,指针指向的底层 hmap 并没有改变,所以可以在函数内部改变 map

  • 引用是变量的别名,通过引用可以直接操控变量本身(快捷方式)
  • 指针储存一个内存地址

对象池

https://pedrogao.github.io/posts/go/pool.html

内存池

https://zhuanlan.zhihu.com/p/572059278

rune和byte有什么区别

类型不同:

  • byte:字节,是uint8的别名类型
  • rune:字符,是int32的别名类型

储存的字符不同:

  • byte用于表示ASCII码字符,0-255
  • rune用于表示Unicode字符,可以储存任意Unicode字符

占用的字节大小不同

  • byte:1
  • rune:4

字符串

八位字节集合,可为空,不为nil,本质上是一个byte类型数组,不可变,并发安全,每次修改字符串都需要重新分配内存。之前的空间会被回收。

拼接方式:

var s string
s += "a"
 
fmt.Sprintf("%s%s", s, "a")
 
var builder strings.Builder
builder.WriteString("a")
builder.String()
 
buf := new(bytes.Buffer)
buf.WriteString("a")
buf.String()
 
baseSlice := []string{"a", "b"}
strings.Join(baseSlice, "")
 
buf := make([]byte, 0)
base = "a"
buf = append(buf, base...)
string(base)

排序: jion = builder > buffer > []byte转换string > + >fmt.sprintf

  • builder会预分配空间,减少扩容,效率更高,适合长字符串操作
  • buffer主要用于处理单个字符,可以针对单个byte进行删除替换操作

数组和切片的区别

  • 数组定长,切片大小可变,自动扩容
  • 作为函数参数,数组传值,切片传引用

切片扩容/缩容

  • 添加元素容量不足触发扩容,分配一个新的数组,小于1024扩容1倍,大于1024扩容1/4倍
  • 删除元素时切片长度小于容量的1/4触发缩容,分配一个新的数组,小于1024长度不变,大于1024缩容1/2。

浅拷贝与深拷贝

  • 值类型每次拷贝都会申请一块新的地址
  • 引用类型浅拷贝只改变指针的指向(地址),深拷贝会申请一块新的地址

make和new有什么区别

  • new给任意类型分配地址,初始化零值,返回指针
  • make用于创建并初始化引用类型的变量,如slice、channel,map,返回引用

context

场景介绍
超时处理通过使用 context 可以方便地设置超时时间,在超时后自动终止协程
终止协程通过使用 cancel() 方法,协程可以很方便地终止
传递数据我们可以将数据写入 context, 在不同协程间传递数据

Map是并发安全的吗

Map不是并发安全

  • 手动加读写锁
  • 使用sync.Map

Map的key删除后内存会释放吗

  • value为值类型,则map的内存不会自动释放
  • value为引用类型,则map的内存会自动释放
  • map设为nil,内存回收

Map产生的panic能被recover吗

Map 由于并发读写导致的 panic 是不能被 recover 的,因为 Map 的异常使用 runtime.throw() 抛出,这类异常不能被 recover。

泛型和接口有什么区别

  • 接口是在运行时实现的,泛型是在编译时实现的,泛型在编译时会编译成具体的类型,所以泛型性能更好
  • 接口主要用来约束方法,用于定义对象的行为,泛型主要用来约束类型,让代码可以处理多种类型
  • 接口是go语言的原生特性,泛型是1.18版本引入
  • 泛型引发的错误在编译时能发现,而接口在运行时才能发现

其它

  • 对未初始化的Map赋值会引发panic
  • 对未初始化的Map删除会引发Panic(早期版本,现版本不会)
  • 对未初始化的Map读取key会返回当前类型的空值
  • 执行for range时遍历的时副本
  • defer执行顺序先进后出
  • Map 会自动扩容,但是不会自动缩容。
  • channel 是线程安全的,原因是 channel 内部实现了锁的机制
  • Map 在内部使用哈希算法放置元素,在自动扩容时又会重新计算哈希值,因此元素的地址会不断变化,官方为了避免用户认为 Map 元素排列是有序的,直接采用随机顺序返回,所以遍历是无序的