本资源由会员分享,可在线阅读,更多相关《golang基础面试题完整(28页珍藏版)》请在人人文库网上搜索。

golang基础⾯试题启动流程Q.go的init函数是什么时候执⾏的? Q.多个init函数执⾏顺序能保证吗?Q.go init 的执⾏顺序,注意是不按导⼊规则的(这⾥是编译时按⽂件名的顺序执⾏的) Q.init函数能被外部调⽤吗?内存分配[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-p22eNrMY-1646530923570) (C:\Users\MSI\AppData\Local\Temp\1646493064752.png)]代已未堆栈环命                                                                               :mspan在arena区),bitmap区16GB,arena区512GB 内存管理组件:mcache⽆锁分配, mcentral, mheapmspan 是 双 向 链 表                                                  tiny分配器,减少内存浪费率,但回收困难,所有对象可回收才能回收      span⾄少1个page(8k),被划分成固定⼤⼩的slot,bitmap表⽰slotmchache⽆锁分配 ,mcentral,mheap.>32kb,从mheap从获取.<16且⽆指针,使⽤tiny分配器.<16有指针或者16-32kb,从mchache中获取mspan中的slot newnew和make有什么区别? newobject函数make Q.go对象在内存中是怎样的                 Q.go的内存分配是怎么样的                    Q.栈的内存是怎么分配的                      Q. 检 测 Golang 内 存 泄 漏 的 ⼯ 具 A.pprof,trace,race检测Q.go                               struct 能 不 能 ⽐ 较 ? ⾥的逃逸分析是什么?怎么避免内存逃逸?Q.简单介绍⼀下go的内存分配机制? 有mcentral为啥要mcache?A.答了 mcentral是服务所有系统线程,mcache为系统线程独享,mcache缺少span时去mcentral->mheap 中取Q.内存对齐              跟c++的⼀样,8字节对齐Q.go 内存分配,和 tcmalloc 的区别A.借⽤了tcmalloc的思想(固定分区、动态分区、页式分配) A.是操作符还是内置函数A. 内 置 函 数           Q.Go怎么做深拷贝         A.先序列化,再反序列化Q.了解内存泄漏吗?有什么危害? A.占资源,甚⾄程序奔溃      Q.空结构体的⽤处A.1.map 。             是空结构体,构造集合。2.通道。不管切⽚多长,都不会占⽤空间。4.仅包含⽅法的结构体。不⽤指针,节约空间。5.最后零字段。final zero field:结构体⾥的最后⼀个属性如果是空结构体,会当成1个字节处理。如果结构体嵌套的全是空结构体,还是0个字节。Q.golang如何确定有没有内存泄露?系统⾥怎么去监控整体的运⾏情况?⽇志是怎么处理的?A.Q.虚拟内存地址Q.char *Ptr=0; *Ptr=‘a’ 说明⼀下内存分配流程A. 报 错 。                                    A.⽅法内变量是值,返回了变量地址,这个时候会出现内存逃逸 Q.逃逸分析说下?为什么要逃逸分析?如何避免逃逸        A.少⽤指针Q.连接池的好处       复⽤,减少资源消耗。Q.byte 和 rune 有 什 么 区 别         A.rune是int32,占⽤的字节数不⼀样Q.malloc(?啥玩意不会) A.极⼩分配器,会内存合并Q.Go语⾔内存分配,什么分配在堆上,什么分配在栈上。【我顺便提了⼀下内存逃逸,应该算加分了嘻嘻】   A.局部变量,⼊参,全局变量存在栈上。引⽤类型存堆上。数据在内存中的存储形式A.能说说栈在实际中的应⽤吗A.Q.A.Q.go的启动过程A.Q.go怎么实现封装继承多态A.⽤struct模拟类。Q. 知道浮点数在机器上⾯怎么存储的A.符号位,阶码,尾数Q.go语⾔的时候垃圾回收,写代码的时候如何减少⼩对象分配 A.Q.简单介绍⼀下go的内存分配机制? 有mcentral为啥要mcache?答了mcentral是服务所有系统线程,mcache为系统线程独享,mcache缺少span时去mcentral->mheap中取Q.当写⼀个程序申请内存时,会做哪些操作? A.中断,页⾯置换,堆,栈等。            Q.说⼀下 string 和 []byte 的⾼效转换Q.Go的数据结构的零值是什么      A.引⽤类型是nil,其他是0,0.0,""等等垃圾回收1.3 版本是标记清除算法,stw时间过长STW,stop the world;让程序暂停,程序出现卡顿 (重要问题)。标记需要扫描整个heap清除数据会产⽣heap碎⽚gcStart三⾊标记法,“强-弱” 三⾊不变式、插⼊屏障、删除屏障、混合写屏障、STW 有两个问题, 在三⾊标记法中,是不希望被发⽣的条件1: ⼀个⽩⾊对象被⿊⾊对象引⽤(⽩⾊被挂在⿊⾊下)条件2: 灰⾊对象与它之间的可达关系的⽩⾊对象遭到破坏(灰⾊同时丢了该⽩⾊) 当以上两个条件同时满⾜时, 就会出现对象丢失现象!插⼊写屏障:A引⽤C,A⿊C⽩,会把C加⼊写屏障buf,最终flush到扫描队列,stw 不⾜:结束时需要stw重新扫描栈,⼤约需要10~100ms删除屏障:具体操作:  被删除的对象,如果⾃⾝为灰⾊或者⽩⾊,那么被标记为灰⾊。回收精度低。满⾜: 弱三⾊不变式. (保护灰⾊对象到⽩⾊对象的路径不会断)插⼊写屏障和删除写屏障的短板:插⼊写屏障:结束时需要STW来重新扫描栈,标记栈上引⽤的⽩⾊对象的存活;删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。混合写屏障:前提条件栈引⽤的对象都是⿊⾊,后来添加的依然是⿊⾊。span⾥有gcmarkBits,0⽩⾊,1灰⾊或⿊⾊三⾊标记:通过mspan查看是否被引⽤灰⾊:对象已被标记,但这个对象包含的⼦对象未标记⿊⾊:对象已被标记,且这个对象包含的⼦对象也已标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)⽩⾊:对象未被标记,gcmarkBits对应的位为0(该对象将会在本次GC中被清理)例如,当前内存中有A~F⼀共6个对象,根对象a,b本⾝为栈上分配的局部变量,根对象a、b分别引⽤了对象A、B,   ⽽B对象⼜引⽤了对象D,则GC开始前各对象的状态如下图所⽰:初始状态下所有对象都是⽩⾊的。接着开始扫描根对象a、b;    由于根对象引⽤了对象A、B,那么A、B变为灰⾊对象,接下来就开始分析灰⾊对象,分析A时,A没有引⽤其他 对象很快就转⼊⿊⾊,B引⽤了D,则B转⼊⿊⾊的同时还需要将D转为灰⾊,进⾏接下来的分析。D没有引⽤其他对象,所以D转⼊⿊⾊。标记过程结束 最终,⿊⾊的对象会被保留下来,⽩⾊对象会被回收掉。root对象:栈,全局对象等Q.gc触发时机              GOGC⽐例后台触发-sysmon检测⼿ 动 触 发 -runtime.GC() Q.Golang 的 GC 触发时机是什么A.阈值触发;主动触发;两分钟定时触发; Q.执⾏时为啥需要STW?A.漏标,多标   Q.如何解决漏标?A.满⾜三⾊不变性              Q.如何满⾜三⾊不变性?        A.屏障技术。                   Q.gc怎么帮我们回收对象A.三⾊标记Q.go的gc会不会漏掉对象或者回收还在⽤的对象?       A.不会回收还在⽤的对象,如果有这个问题,早就歇菜了。 当前gc漏掉是有,但在下⼀次gc的时候就不会漏掉。    Q.gc会不会太慢,跟不上内存分配的速度?            A.STW会暂停,肯定有影响。不过最新的垃圾回收机制将STW时间降低到了极致。Q.如果a=5, b=a , c = &a ,gc启动后,a,b,c是什么颜⾊的?为什么?A.Q.go 的 GC 和 Python 的 GC                        A.go是可达性分析,Python是引⽤标记。             是为了什么?了解新版本go对写屏障的改进吗? A.扫描栈会STW。新版本是混合写屏障,不需要重新扫描栈。                       Q.两次GC周期重叠会引发什么问题,GC触发机制是什么样的?                      Q.那如果⽤户在并发CMS期间改了引⽤,写屏障如何保证三⾊不变性?               A.插⼊屏障和删除屏障共同保证Q.pprof使⽤A.调度器newprocrunnext   123→312 排队逻辑,g调度逻辑GODEBUG=schedtrace=1000 可执⾏程序M⽐P多,不会多太多,最⼤1万个Q.go调度都发⽣了啥A.gmp⽹络和锁会不会阻塞线程A.会。Q.什么时候阻塞线程A.休眠,加锁等等。Q.协程同步的⽅式   waitgroup和context区别A.waitgroup收集次数,context不收集次数             Q.什么场景不适合⽤协程?那该⽤什么实现并发呢?(epoll) A.Q.创建多个goroutine在多核的情况下是如何分配的A.gmpQ.⼀个main函数内⽤go 开启多个协程,现在⼀个协程panic了,main函数会怎样? 为什么? A.整个程序就奔溃了Q.Golang 的协程间通讯⽅式有哪些? 共享内存和协程通信。Q.说⼀下go协程设计A.由于⾃⼰⽤c实现过协程,所以答的很随意。讲了⼀下函数调⽤约定,栈布局,上下⽂切换,x86寄存器,⼜讲了⼀下⽤gcc的"- finstrument-functions"。然后讲了⼀下go的调度思路。参考go源码runtime.schedule Q.go实现协程池           channel实现。Q.golang gmp模型怎么调度的,⼀个goroutine sleep了,操作系统是怎么唤醒的A.信号量、互斥锁、条件变量M,那能有⼏个groutinue,groutinue数量的上限是多少?    A.理论⽆上限什么时候会被挂起A. ⾮ io                   Q.主线程控制协程的⽅法A.泄露,或是内存泄漏。A.Q.go 的 goroutine                         , 如 何 停 ⽌ ⼀ 个 goroutine? goroutine的退出,可以⽤channel和Context) Q.起⼏个线程死循环 cpu会爆吗?,8个协程就能让cpu100% Q.如何实现只开100个协程               A. ⽤ channel 控 制 , 容 量 是 100 Q.golang如何知道或者检测死锁A.pprof          怎么操作内核线程A.Q.⽣产中哪些服务⽤的 进程、线程,为什么要这么做,有什么好处A.Q.什么情况下 M 会进⼊⾃旋的状态?A.(MGMM 需要处理)Q.Golang 怎么在并发编程中等待多个 goroutine 结束? A.channel,sync.WaitGroup                        Q.怎么限制goroutine的数量?A.channelQ.golang调度 能不能不要p A.可以不要,但不好控制。Q.协程泄***r>如果你启动了⼀个  goroutine,但并没有符合预期的退出,直到程序结束,此goroutine才退出,这种情况就是  goroutine 泄露。当 goroutine 泄露发⽣时,该 goroutine 的栈(⼀般 2k 内存空间起)⼀直被占⽤不能释放,goroutine ⾥的函数在堆上申请的空间也不能被 垃圾回收器 回收。A.Q.协程中参数直接使⽤,和传参的区别是什么?为什么会造成这种结果。 A.直接使⽤是闭包,传参跟普通函数⼀样了。Q.为什么P的local queue可⽆锁访问,任务窃取的时候要加锁吗? A.⽤casgo协程池,怎么实现的?协程池上限确定的原则是什么?  A.goroutine泄漏的例⼦? A.Q.goroutine和线程的区别:goroutine    是建⽴在线程之上的轻量级的抽象,允许以⾮常低的代价在同⼀地址空间中并⾏地执⾏多个函数和⽅法。相⽐如线程,创建和销毁的代价更⼩,调度是独⽴于线程之外的。Q.为什么说goroutine轻量:创建Goroutine 通常只需要2kb 的内存,但是线程则需要1mb 2 . go 中创建和销毁都是⾃⼰管理的,⽽不是像操作系统申请资源,销毁再归还。3 GMP调度Q.协程内部再启⽤协程,它们是没关系,对吧?外⾯的协程奔溃,⾥⾯的还会执⾏吗?外部协程执⾏结束的时候,如何让内部协程也停⽌运⾏?golang原⽣提供的包⾥,让内部协程停⽌运⾏。context打配合。Q.协程直接如何通信?            A.共享内存和通道                Q.进程间的通信⽅式A.Q.进程、线程、GoroutineQ.线程什么是私有的A. 获取返回值(捕获参数),有哪些⽅案A.( 全 局 参 数 、 channel, 闭 包 )                      Q.gmp当⼀个g堵塞时,g、m、p会发⽣什么                 A.cpu密集型:p和g分离,io密集型:p和mg分离            创建协程的过程知道吗A.Q.线程池是核⼼态还是⽤户态创建的?A.线程的切换是在核⼼态吗? A.g会怎么处理 A.放全局队列Q.go调度中阻塞都有那些⽅式A.通道,睡眠,Q.⽗goroutine退出,如何使得⼦goroutine也退出? A.context                                            两个协程,其中⼀个协程在死循环,会发⽣什么A.p和g分离,g放在全局队列                           Q.⽤Channel和两个协程实现数组相加A.⽤协程实现顺序打印123 A>goroutine A.实现⼀个协程池,⼤概⽤什么实现A.goroutine A.Qgo⾥⾯goroutine创建数量有限制吗? A.⽀持哪些并发机制A.服务能开多少个m由什么决定A.Q.开多少个p由什么决定A.和p是什么样的关系A.同时启了⼀万个g,如何调度的? A.goroutine的返回值,如何区别他们 A.创建数量有限制吗? A.G会不会饥饿,为什么?P的数量是多少?能修改吗?M的数量是多少?   A.1:1吗?如果⼀个G阻塞了会怎么样? A.?chan、sync包A.读请求,剩余900个写协程阻塞了,如何设计避免阻塞? A.低优先级任务,其他为⾼优先级任务,双核CPU如何调度最为⾼效。A.Q.协程与线程区别?为什么快,快在哪了?⾃定义了4个伪寄存器 fp sp sb pc,轻量,初始2KB动态伸缩,由runtime管理,对os透明等等各种原因,⼜说了以下gmp调度的优 势。追问寄存器是什么,你对寄存器还有什么了解。我说了⼀点关于go和c    在寄存器⽅⾯使⽤的不同,函数传参,调⽤堆栈等等,扯了点汇编区别,就⼜问了⼏个汇编指令,过了并发GMP版本迭代的过程?说说如何避免全局队列饥饿?  A.问:stack⾥⾯保存什么?答:method栈帧,pc,局部变量。问:局部变量是什么?应该不会保存在stack⾥⾯吧?答:java分配对象内存是在堆⾥⾯的,你可以理解为⼀个指针。 问:协程怎么停顿?答:抛异常造成suspend,保存现场。问:协程为什么⾼效?答:避免内核中线程的上下⽂切换、协程数据更轻量。问:还有吗?等了⼀会我没思路,然后⾯试官⾃⼰说了其实还有⼀个,线程是可以随时发⽣上下⽂切换的,⽽协程是需要在固定位置显式切    换的,所以保存上下⽂更轻量。Q.什么时候⽤多线程,什么时候⽤多进程A.Q.IO 密集型和 CPU 密集型如何分配线程优先级A.Q. go语⾔的并发问题,⾯试官输⼊了⼀段代码: func main() {for i := 0; i < 10; i++ { go fmt.Println(i)}}A.主协程睡眠的情况下是乱序。⼀定会输出0-9的数字。原因在于i传参了。 不睡眠就没输出。Q.Go 的 协 程 可 以 不 可 以 ⾃ ⼰ 让 出 cpu A.runtime.Gosched()                         Q.Go的协程可以只挂在⼀个线程上⾯吗             P。runtime.GOMAXPROCS(1) Q.⼀个协程挂起换⼊另外⼀个协程是什么过程?A.Q. Go⾥⾯⼀个协程能保证绑定在⼀个内核线程上⾯的。A.Q.如何让n个线程执⾏完后⼀起结束A.sync.WaitGroupchannelQ.在 IO 密集型场景下,可以适当调⾼ P 的数量A.在 IO 密集型场景下,可以适当调⾼ P 的数量通道创建chansend1,chanrecv1对⼀个关闭的通道再发送值就会导致panic。对⼀个关闭的通道进⾏接收会⼀直获取值直到通道为空。操作 nil channel操作 nil channelclosed channelnot nil, not closed channelclosepanicpanic正常关闭读 <- ch阻塞读到对应类型的零值channelchannel写 ch <-阻塞panic阻塞或正常写⼊数据。⾮缓冲型 channel 没有等待接收者或缓冲型 channel buf 满时会被阻塞数据交流:当作并发的 buffer 或者 queue,解决⽣产者 - 消费者问题。多个goroutine 可以并发当作⽣产者(Producer)和消费者(Consumer)。数据传递:⼀个 goroutine 将数据交给另⼀个 goroutine,相当于把数据的拥有权 (引⽤) 托付出去。信号通知:⼀个 goroutine 可以将信号 (closing、closed、data ready 等) 传递给另⼀个或者另⼀组 goroutine 。任务编排:可以让⼀组 goroutine 按照⼀定的顺序并发或者串⾏的执⾏,这就是编排的功能。锁:利⽤ Channel 也可以实现互斥锁的机制。Q.优雅关闭channel?A.如果sender(发送者)只是唯⼀的sender或者是channel最后⼀个活跃的sender,那么你应该在sender的goroutine关闭channel 不要从⼀个 receiver 侧关闭 channel,也不要在有多个 sender 时,关闭 channel。sync.Once封装close⽅法根据 sender 和 receiver 的个数,分下⾯⼏种情况:⼀个 sender,⼀个 receiver⼀个 sender, M 个 receiver N 个 sender,⼀个 reciverN 个 sender, M 个 receiver对于 1,2,只有⼀个 sender 的情况就不⽤说了,直接从 sender 端关闭就好了,没有问题。重点关注第 3,4 种情况。第3种情况,解决⽅案就是增加⼀个传递关闭信号的 channel,receiver 通过信号 channel 下达关闭数据 channel 指令。senders 监听到关闭信号后,停⽌发送数据。并不主动关闭。Q.⽤go撸⼀个⽣产者消费型,⽤channel通信,怎么友好的关闭chan.             Q.有⼀道经典的使⽤ Channel 进⾏任务编排的题,你可以尝试做⼀下:有四个goroutine,编号为 1、2、3、4。每秒钟会有⼀个 goroutine 打印出它⾃⼰的编号,要求你编写⼀个程序,让输出的编号总是按照 1、2、3、4、1、2、3、4、……的顺序打印出来。Q.开俩个协程,⼀个协程⽣产数据,另⼀个协程对数据进⾏处理,处理完后再把数据发回去,使⽤管道如何实现? Q.channel实现原理,为什么不⽤加锁?关闭以后,再往其发送或接收,会发⽣什么 Q.连续关闭两次管道会发⽣什么                  Q.如何判断channel是否关闭?Q.channel底层实现 :buf,sendx,recvx,lock, sendq , recvq ; hchan 结构体并发安全:CSP channel 通信加锁Q.Channel 的 阻 塞 和 ⾮ 阻 塞 ( 顺 带 问 了 select ⽤ 法 ) Q.channel 了 解 吗 ,channel 的 ⼯ 作 原 理 是 什 么 ?    对已经关闭的channel进⾏读写操作会发⽣什么Q.channel的实现原理?答了环形队列,被追问为什么⽤环形队列  Q.写个channel相关的题,并发模型,爬⾍url,控制并发量       Q.关闭channel有什么作⽤?Q.被close的channel会有什么问题                        Q.分布式锁知道哪些?⽤channel如何实现?               来计数,再开启⼀个协程wg.wait()监听所有⽣产者。⾯试官说不是他想     要的。是想要atomic来计数吗?求⼤佬解惑。⾯试超时反问时没时间问了。。)Q.⼿写⽣产者消费者模型Q.写多⽣产者多消费者模型延迟Q. 如 何 处 理 异 常                                                                 defer defer进⾏解锁和⾃⼰⼿动解锁有什么区别? Q.defer函数的使⽤场景(延迟Close、recover panic)Q.defer的执⾏顺序?Q.go 语⾔的 panic 如何恢复Q.defer关键字后的函数在什么时候调⽤ 主函数return前还是return后: defer的执⾏顺序在return之后,但是在返回值返回给调⽤⽅之前,所以使⽤defer可以达到修改返回值的⽬的。Q.defer  A  ;  defer  B  ;  defer  panic("")  A和B能不能执⾏到Q.defer的执⾏流程                                                  Q.怎么保存在程序崩溃时的数据,当时没理解到,我觉得是(defer+reciver)切⽚切⽚结构type slice struct {array unsafe.Pointer//指针len int//长度cap int//容量}// An notInHeapSlice is a slice backed by go:notinheap memory. type notInHeapSlice struct {array *notInHeap//指针len int//长度cap int//容量}//nil切⽚可以append//※创建切⽚func makeslice(et *_type, len, cap int) unsafe.Pointer {// 所需要分配的内存⼤⼩mem, overflow := math.MulUintptr(et.size, uintptr(cap))//len > cap {// NOTE: Produce a ‘len out of range’ error instead of a// ‘cap out of range’ error when someone does make([]T, bignumber).// ‘cap out of range’ is true too, but since the cap is only being// supplied implicitly, saying len is clearer.// See /issue/4085.mem, overflow := math.MulUintptr(et.size, uintptr(len))//}panicmakeslicecap()}}扩容策略如果2倍容量还⼩于新增的容量,那就取新增的容量如果新容量⼤于原来容量的⼆倍直接⽤新容量。如果原来切⽚的容量⼩于1024,于是扩容的时候就翻倍增加容量。⼀旦元素个数超过 1024,那么增长因⼦就变成 1.25 ,即每次增加原来容量的四分之⼀。//扩容// growslice handles slice growth during append.// It is passed the slice element type, the old slice, and the desired new minimum capacity,// and it returns a new slice with at least that capacity, with the old data// copied into it.// The new slice’s length is set to the old slice’s length,// NOT to the new requested capacity.// This is for codegen convenience. The old slice’s length is used immediately// to calculate where to write new values during an append.// TODO: When the old backend is gone, reconsider this decision.// The SSA backend might prefer the new length or to return only ptr/cap and save stack space. func growslice(et *_type, old slice, cap int) slice {Q.go的 slice扩容机制,slice 在for⼀遍会改变内容吗Q.Slice是否线程安全Q.讲下golang的slice和string底层,空slice和nil的slice区别?能直接append吗?扩容?Q.Golang slice 不断 append,是如何给它分配内存的?slice 如果分配的 capacity 是 10,那当容量到多⼤的时候才会扩容?8、9、10?Q.切⽚和数组差别Q.如何删除slice中间的元素(s = append(s[:i],s[i+1,]…),我感觉其实就是切⽚的应⽤。Q.Go string底层实现?Q.[]byte和string互转的⾼效⽅法Q.那你这个⽤unsafe.Pointer和uintptr的⽅案,不会有问题吗?string少的那个cap字段怎么填充?(我答可能GC会有点影响,因为unsafe.Pointer指向的对象不会被GC回收了)Q.string类型转为[]byte过程发⽣了什么Q.slice作为函数参数怎么解决上⾯的问题? Q.数组相⽐slice的优点在哪⾥A. 可 ⽐ 较 编 译 安 全 长度是类型规划内存布局访问速度map使⽤ map 的 2 种常见错误常见错误⼀:未初始化Q.hash冲突的解决办法有哪些?                                                                   Q.项⽬⾥的map并发怎么做? 为啥⽤分段锁不⽤sync.map? 分段锁拆了⼏个分⽚? Q.hashmap数据太多 rehash时间长 怎么解决Q.rehash 时 候 可 以 get                   put 吗Q.golang中两个map对象如何⽐较                       Q.哈希的实现有哪⼏种,如何取hashcode,冲突检测⼏种⽅法Q.map遍历的时候每次顺序都是固定的吗?为什么?   Q.Map可以⽤数组作为Key吗(数组可以,切⽚不可以)    Q.如何实现Map的有序查找(利⽤⼀个辅助slice)(sync.Map,底层实际上⽤了⼀个Map缓存) Q. 扩 容 机 制 ?                                      Q.sync.map与map的区别,怎么实现的【答了个⼤概,这个没答好】Q.map哈希过程(讲错了⼀点点,忘了可能插⼊相同键不同值)       Q.哈希过程是什么样⼦的Q.桶的增加(这个具体还挺复杂的)                    Q.map中对于key的哈希是怎么计算的?                 然后修改这个值,原map数据的值会不会变化Q.Hash 实 现 、 冲 突 解 决 、 应 ⽤                    Q.map⾥⾯解决hash冲突怎么做的,冲突了元素放在头还是尾Q.项⽬⾥的map并发怎么做? 为啥⽤分段锁不⽤sync.map?分段锁拆了⼏个分⽚?Q. hash冲突解决办法,有什么弊端Q. map⾥⾯解决hash冲突怎么做的,冲突了元素放在头还是尾接⼝接⼝不能直接使⽤接收者为值类型的⽅法。Q.go⾥⾯interface是什么概念         Q.那你说下interface底层实现Q.go语⾔中结构体指针为空,赋给⼀个interface{}为什么interface不为空Q.interface 不是个好的形式,会导致 GC 压⼒⼤,为啥,那⽤什么形式⽐较好反射Q.golang类型断⾔,怎么⽤                   selectQ.golang的多路复⽤A.select channelQ.Go语⾔的Select 与 I/O多路复⽤的Select区别Q.select的⽤法,加上default⼜会怎么样?context上下⽂信息传递goroutine得运⾏超时控制的⽅法调⽤Q.使⽤ WithCancel 和 WithValue 写⼀个级联的使⽤ Context 的例⼦,验证⼀下 parent Context 被 cancel 后,⼦ conext 是否也⽴刻被 cancel 了。Q.问了context的作⽤,哪些场景使⽤过context? Q.context包内部如何实现的?Q.go 怎么控制查询timeout (context) Q.context包的⽤途?Q.Context 包的作⽤编译时做接⼝检查(PSE.gvar _ InterfaceName(*TypeName)(nil)) 运⾏时做接⼝检查(PSE.g_okTypeName.(InterfaceName))heap list ring(我听到这个问题懵了⼀下?然后就基于ListNode和List结构体,说了⼀下,    然后在List结构体⾥保存头尾指针这样)                                                                                    B站app的页⾯分区怎么设计(这个⼀开始没想到应该怎么回答,最后回答的是数据库表的设计,主键和外键)同步库条件变量CondQ.⼀个 Cond 的 waiter 被唤醒的时候,为什么需要再检查等待条件,⽽不是唤醒后进⾏下⼀步?Q.你能否利⽤ Cond 实现⼀个容量有限的 queue?映射Q.为什么 sync.Map 中的集合核⼼⽅法的实现中,如果 read 中项⽬不存在,加锁后还要双检查,再检查⼀次 read?Q.你看到 sync.map 元素删除的时候只是把它的值设置为 nil,那么什么时候这个 key 才会真正从 map 对象中删除?锁go run -race counter.go开启了 race 的程序部署在线上,还是⽐较影响性能的。运⾏ go tool compile -race -S counter.go,可以查看计数器例⼦的代码,重点关注⼀下 count++ 前后的编译后的代码在编译的代码中,增加了 runtime.racefuncenter、runtime.raceread、runtime.racewrite、runtime.racefuncexit 等检测 data race 的⽅法。通过这些插⼊的指令,Go race detector ⼯具就能够成功地检测出 data race 问题了互斥量type type Mutex struct state int32 sema uint32}const (mutexLocked = 1 << iota // 持有锁的标记mutexWoken // 唤醒标记mutexStarving // 从state字段中分出⼀个饥饿标记mutexWaiterShift // 阻塞等待的waiter数量starvationThresholdNs = 1e6)Q.⽬前 Mutex 的 state 字段有⼏个意义,这⼏个意义分别是由哪些字段表⽰的? A.可重⼊锁,try锁(通过 unsafe 的⽅式读取到 Mutex 内部的 state 字段,),需要⼿动实现Q.等待⼀个 Mutex 的 goroutine 数最⼤是多少?是否能满⾜现实的需求? Q.常见的 4 种错误场景Lock/Unlock 不是成对出现2.CopyMutexgovetGo4.死锁过 Go 运⾏时⾃带的死锁检测⼯具,或者是第三⽅的⼯具(⽐如Go pprof ⼯具分析,它提供了⼀个 block profiler 监控阻塞的Q.如何实现可重⼊锁?⽅案⼀:通过 hacker 的⽅式获取到 goroutine id,记录下获取锁的 goroutine id,它可以实现 Locker 接⼝。runtime.Stack ⽅法获取栈帧信息,栈帧信息⾥包含 goroutine id。petermattis/goid也可以获取goid⽅案⼆:调⽤ Lock/Unlock ⽅法时,由 goroutine 提供⼀个 token,⽤来标识它⾃⼰,⽽不是我们通过 hacker 的⽅式获取到 goroutine id,但是,这样⼀来,就不满⾜Locker 接⼝了。//go get -u /petermattis/goid//go get -u /petermattis/goid// RecursiveMutex 包装⼀个Mutex,实现可重⼊type RecursiveMutex struct { sync.Mutexowner int64 // goroutine idrecursion int32 // 这个goroutine 重⼊的次数}func (m *RecursiveMutex ) Lock() { gid := goid.Get()// 如果当前持有锁的goroutine 就是这次调⽤的goroutine, 说明是重⼊if atomic.LoadInt64 (&m.owner) == gid { m.recursion ++return}m.Mutex.Lock()// 获得锁的goroutine 第⼀次调⽤,记录下它的goroutine id, 调⽤次数加1atomic.StoreInt64 (&m.owner, gid) m.recursion =1}func (m *RecursiveMutex ) Unlock() { gid := goid.Get()// ⾮持有锁的goroutine 尝试释放锁,错误的使⽤if atomic.LoadInt64 (&m.owner) != gid { panic(fmt.Sprintf("wrong the owner(%d): %d!" , m.owner, gid))}// 1m.recursion --if m.recursion != 0 { // 如果这个goroutine 还没有完全释放,则直接返回return}// 此goroutine 最后⼀次调⽤,需要释放锁atomic.StoreInt64 (&m.owner, -1) m.Mutex.Unlock()}// Token // Token ⽅式的递归锁type TokenRecursiveMutex struct { sync.Mutextoken int64 recursion }// 请求锁,需要传⼊tokenfunc (m *TokenRecursiveMutex ) Lock(token int64) {if atomic.LoadInt64 (&m.token) == token { //如果传⼊的token和持有锁的token⼀致,m.recursion ++ return}m.Mutex.Lock() // 传⼊的token不⼀致,说明不是递归调⽤// 抢 到 锁 之 后 记 录 这 个 token atomic.StoreInt64 (&m.token, token) m.recursion = 1}// 释放锁func (m *TokenRecursiveMutex ) Unlock(token int64) {if atomic.LoadInt64 (&m.token) != token { // 释放其它token持有的锁panic(fmt.Sprintf("wrong the owner(%d): %d!" , m.token, token))}m.recursion --     // token释放锁if m.recursion != 0 { // 还没有回退到最初的递归调⽤return}atomic.StoreInt64 (&m.token, 0) // 没有递归调⽤了,释放锁m.Mutex.Unlock()}Q.你可以为 Mutex 获取锁时加上 Timeout 机制吗?会有什么问题吗? Q.互斥锁的底层实现Q.go中的互斥锁:正常、饥饿状态,读写锁中写操作如何阻⽌读操作? Q.go的锁是可重⼊的吗?Q.获取不到锁会⼀直等待吗?  timeout的锁? Q.读写锁说下Q. ⽆ 锁 编 程                  Q.写⼀个会产⽣死锁的代码      Q.sync.Map 的使⽤,与⾃⼰写的阻塞锁有什么区别?那个更快Q. golang中的CAS问题Q.sync 包 了 解 吗 Q.sync pool的实现原理Q.golang中的CAS问题读写锁readers-writers 问题读优先的设计可以提供很⾼的并发性,但是,在竞争激烈的情况下Write-preferring:写优先的设计意味着,如果已经有⼀个 writer 在等待请求锁的话,它会阻⽌新来的请求锁的 reader 获取到锁,所以优先保障 writer。当然,如果有⼀些 reader 已经请求了锁的话,新请求的 writer 也会等待已经存在的 reader 都释放锁之后才能获取。所以,写优先级设计中的优先权是针对新来的请求⽽⾔的。这种设计  主要避免了 writer 的饥饿问题。不指定优先级:这种设计⽐较简单,不区分 reader 和 writer 优先级,某些场景下这种不指定优先级的设计反⽽更有效,因为第⼀类优先级会导致写饥饿,第⼆类优先级可能会导致读饥饿,这种不指定优先级的访问不再区分读    写,⼤家都是同⼀个优先级,解决了饥饿的问题。type RWMutex type RWMutex struct {w Mutex // 互斥锁解决多个writer的竞争writerSem uint32 // writer 信号量readerSem uint32 // reader 信号量readerCount int32 // reader 的数量readerWait int32 // writer等待完成的reader 的数量}const rwmutexMaxReaders = 1 << 30不可复制重⼊导致死锁释放未加锁的RWMutex单例Once// Once is an object that will perform exactly one action.// Once is an object that will perform exactly one action.//// A Once must not be copied after first use.type Once struct {// done indicates whether the action has been performed.// It is first in the struct because it is used in the hot path.// The hot path is inlined at every call site.// Placing done first allows more compact instructions on some architectures (amd64/386),// and fewer instructions (to calculate offset) on other architectures.done uint32 m Mutex}// // ⼀个功能更加强⼤的Oncetype Once struct { m sync.Mutex done uint32}// ferror ,如果初始化失败,需要返回失败error// Do⽅法会把这个error 返回给调⽤者func (o *Once) Do(f func() error ) error {if atomic.LoadUint32 (&o.done) == 1 { //fast pathreturn nil}return o.slowDo(f)}// 如果还没有初始化func (o *Once) slowDo(f func() error ) error { o.m.Lock()defer o.m.Unlock() var err errorif o.done == 0 { // 双检查,还没有初始化err = f()if err == nil { // 初始化成功才将标记置为已初始化atomic.StoreUint32(&o.done, 1)}}return err}第⼀种错误:死锁.once。Do嵌套第⼆种错误:未初始化Q.我已经分析了⼏个并发原语的实现,你可能注意到总是有些 slowXXXX 的⽅法,从XXXX ⽅法中单独抽取出来,你明⽩为什么要这么做吗,有什么好处?Q.Once 在第⼀次使⽤之后,还能复制给其它变量使⽤吗?池bytebufferpool oxtoacart/bpool连接池标准库中的 http client 池TCP 连接池Memcached Client 连接池Worker Pool等待组WaitGroup,相当于java中的CountDownLatchAdd,⽤来设置 WaitGroup 的计数值;type WaitGroup type WaitGroup struct {// 避免复制使⽤的⼀个技巧,可以告vet⼯具违反了复制使⽤的规则noCopy noCopy// 64bit(8bytes) 的值分成两段,⾼32bit是计数值,低32bit是waiter的计数// 另外32bit是⽤作信号量的// 因为64bit值的原⼦操作需要64bit对齐,但是32bit编译器不⽀持,所以数组中的元素在不同的// 总之,会找到对齐的那64bit作为state,其余的32bit做信号量state1 [3]uint32}常见问题⼀:计数器设置为负值常见问题⼀:计数器设置为负值第⼀种⽅法是:调⽤ Add 的时候传递⼀个负数第⼆个⽅法是:调⽤ Done ⽅法的次数过多,超过了 WaitGroup 的计数值。常见问题⼆:不期望的 Add 时机常见问题三:前⼀个 Wait 还没结束就重⽤ WaitGroupnoCopy:辅助 vet 检查Q.通常我们可以把 WaitGroup 的计数值,理解为等待要完成的 waiter 的数量。你可以试着扩展下 WaitGroup,来查询 WaitGroup 的当前的计数值吗?timertime/tick.go time/sleep.go  和runtimeTimer tick多了periodQ.while(tree){sleep(1)}这个会有什么问题Q.sleep底层实现原理设计模式Q.介绍除了单例与⼯⼚模式外的设计模式(消费者模式)grpcQ.grpc⽤的什么协议? 流式rpc是怎么处理的?Q.RPC是基于TCP和UDP?数据传输过程中的简单流程⼜是怎样的?框架Q.看我有使⽤Kratos框架,问这个框架有哪些特性? Q.Gin框架如何保证并发请求消息准确不出错?Q.说到了protobuf 问为什么protobuf这么快 底层有去了解过吗 你觉得解决了什么问题Q.⽤go写的rpc框架的具体功能细节,注册中⼼单机还是分布式的,其中⼀个挂了怎么办?⼀致性,可靠性怎么保证的。超时控制,加锁和    管道⽀持并发,单机(考虑了多机情况,说了已经在todo⾥了)。Q.Go+QML  利⽤cgo实现的跨平台桌⾯应⽤功能,场景,为什么要写这个东西?  我说为了性能,追问性能如何体现,如何测试的,如何优化。答宏观上从任务管理器,top中看,细节上从pprof进⾏性能定位调优,从⽕焰图上看。追问pprof还有什么功能,你⼀般是怎么定位问    题的?回答还是先具体再细节。Q.项⽬的 RPC 怎么实现的;如何保证数据不丢包;数据存储; Q.protobuf为什么快Q.grpc 和 jsonrpc 的 优 劣                              Q.Rpc协议⼀般如何做序列化?grpc是怎么做的?           ⽹络epoll,nonblockpprof,trace,race检测dlv go语⾔调试利器go build --gcflags="-l -N" -o helloworld //去掉优化的编译objdump -d helloworld > att.asm //反汇编成AT&T汇编go tool objdump -S helloworld > plan9.asm // 反 汇 编 成 Plan 9 汇 编Q.组合式继承go tool compile -l -p main main.go go    tool    nm     main.o Q.go有什么的缺陷,你怎么看     A.⽆泛型,错误处理