golang 面试题

1. golang那些类型是引用类型,那些类型是值类型?

  • 引用类型: 指针,map,slice,channel,interface
  • 值类型: 非引用类型

Note: 对于interface类型,其原类型是引用类型那interface就也是引用类型,如果其原类型是值类型,那interface就也是值类型,取决与其原类型。

2. new和make的区别?

  • new可以为任意类型的变量分配内存空间并初始化为对应类型的零值,返回变量的指针。
  • make为map,slice,channel类型分配内存,并进行初始化,返回对应类型的引用。当new为map,slice,channel分配内存时,初始化为对应的零值为nil,当我们操作nil的map,slice或channel时就会因为空指针而panic。

3.golang切片和数组的区别?

切片和数组都是用来存储同一类型的数据集合,但是数组是值类型,而切片是引用类型,并且数组不可动态扩展长度,而切片可以自动扩容。切片的底层实现是通过数组来实现的,切片还比数组多了一个cap容量的概念。

4.切片的扩容方式?

当所需容量大于当前容量,就会扩容,当所需容量大于当前容量的两倍时就会直接扩到所需容量,当len > 1024的,就会每次扩25%直到扩到满足所需容量为止,当长度小于1024的时候,就每次扩两倍容量。并且所需容量不同类型的计算方式还不一样,但是可以保证计算出来的所需容量一定大于或等于真实的所需容量。一个int32类型的切片,其cap为0,len为0,一次性添加5个数据的之后,这时cap会为6,而如果是int16类型的则会为8,真实所需的容量是5。

5.切片扩容所引发的问题

当切片扩容时,会开辟一块新的内存空间,把老地址的切片的内容复制到新地址当中,并让原指针指向新的地址空间。避坑方法:1.使用copy函数,copy一个全新的切片 2.和append一样,去接收所返回的切片

6. 什么是内存逃逸

函数中的一个变量,如果其作用域没有超过该函数,那么该函数的内存就会在栈上分配,否则就会在堆上分配。一个变量的内存到底是在栈上分配,还是在堆上分配,取决于编译器做完逃逸分析之后决定的。

7.什么是深拷贝,什么是浅拷贝

深拷贝就是完全复制一个新的对象,新对象与原对象的存储空间完全不一样,而浅拷贝就是复制一个指针,指针仍然指向原对象的内存空间。在golang中,当我们调用一个函数,传引用类型的时候或者是指针都是浅拷贝,而传其他类型都是深拷贝。

8.调用一个函数传值还是传结构体?

调用一个函数,通常传的是指针,避免深拷贝带来的效率和内存上的消耗,除非我不希望该方法改变我结构体中的内容。接受的话一般接受结构体,避免内存逃逸带来gc上的压力。

Note: 接收参数,gc压力和深拷贝的开销取一个平衡点,其实怎么说应该都不算错,关键是要说明白你为什么要这么做。

9.什么是channel?

channel即管道,是golang的重要核心之一,是golang中协程通信的方式之一。Golang的并发哲学,不要通过内存共享来通信,而是通过通信来实现内存共享,其具体的体现就是channel。传统的mutex锁都是通过共享临界资源区来实现通信,而golang支持通过channel来进行通信,从而实现内存共享。

10.channel有哪几种

channel分为两种,带缓冲的channel和不带缓冲的channel

  • make(chan int),这样创建的是不带缓冲的chan
  • make(chan int, 1),这样创建的是带一个缓冲的chan

11.介绍一下channel的结构体组成(3_2021_11_3)

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
}

 

这里需要了解几个重要的属性,一个是buf指向存储数据的数组,sendx发送数据的索引,recvx接收数据的索引,sendq发送数据的等待队列,recvq接收数据的等待队列以及lock

当一个协程发送数据到带缓冲的channel时,把数据存储在缓冲数组的那个位置由sendx决定。如果缓冲池满了,就会加入到sendq等待队列当中,由接收的协程负责将其唤醒。

如何保证channel的并发安全,就是使用互斥锁来保证的。

12.向channel里面发送数据的逻辑

向channel里面发送数据分为以下几步:

  • 首先看接收区的等待队列是否有正在等待的receiver,如果有的话,则直接把数据发送给receiver,并将其唤醒
  • 然后看是否有缓冲区,如果缓冲区没满的话,则直接把数据放到缓冲区
  • 如果是非阻塞的则直接返回,否则加入到发送区的等待队列当中去

13.goroutine接收的逻辑是怎么样的?

goroutine接受的逻辑和发送的逻辑差不多,分为以下几步:

  • 首先查看sendq队列中是否有等待的g,如果有的话,则直接把等待的g中的数据取出,然后将其唤醒,返回即可
  • 然后查看是否有缓冲区,缓冲队列是否有数据,如果有的话则直接从缓冲区中拿取数据
  • 否则查看是否阻塞,如果阻塞则加入到recvq队列当中,否则直接返回

14.golang的select当有多个goroutine准备就绪,它是如何选择的?

select语句是用来处理与channel IO相关的逻辑,当有多个channel准备就绪的时候,其是伪随机选择一个goroutine来接收,然后执行相关的语句块。

Note: select关键字常用于和goroutine超时相关的逻辑设计。

15.golang什么时候会panic?

这里总结了8种,应付面试官应该是够了

  • nil空指针异常
  • 数组切片越界
  • 向未初始化的map赋值
  • 并发的写map
  • 关闭未初始化的channel,重复关闭channel,向关闭的channel写数据
  • 死锁,channel只有发送而没有接收
  • interface类型断言失败
  • 递归死循环,堆栈溢出

16.子协程出现panic能在父协程使用recover()捕获吗?

不能,只能在子协程内部使用recover()捕获panic,协程只能捕获自己的panic。

17.什么样的panic不可恢复

  • 并发读写map
  • 递归死循环
  • 死锁

18.unsafe.Pointer和uintptr是用来干什么的呢?

在golang中,为了安全性,是不允许指针像C++那样进行类型转换以及计算的,但是有些场景又必须要这么做怎么办呢?于是出现了unsafe.Pointer用于指针类型转换,比如*int64可以转换为*int64,出现了uintptr用于指针运算。

对于unsafe.Point有以下几点性质:

  • 任意类型的指针都能转化成unsafe.Point
  • unsafe.Point能转化成任意类型的指针
  • unsafe.Point可以转化成uintptr
  • uintptr可以转化成unsafe.Point

19.常用unsafe.Point和uintptr做什么呢,这么做有什么好处呢?

unsafe.Point常用于操作结构体的私有变量,以及类型转换。

好处就是golang中只有unsafe.Point能做到这个事,其他方法都做不到,反射的底层也是用unsafe.Point做的。

20.unsafe.Point和unintptr有什么坑呢?

千万要小心,不要为uintptr起一个中间变量,例如这样:

u := uintptr(unsafe.Pointer(student))
age := (*int)(unsafe.Pointer(u))

这是因为当发生gc的时候,可能会修改变量的内存地址,同时也会修改指向该变量的指针指向新的地址。但是uintptr是一个整数,其不是一个指针,因此在gc修改变量的时候,可不会修改它的值,他还指向原来的地址,然后转化成unsafe.Point进行操作,当然会报错。

posted @ 2022-10-13 15:43  Mr.peter  阅读(817)  评论(0编辑  收藏  举报