当前位置: 首页 > news >正文

“Go程序员面试笔试宝典”复习便签

一.逃逸分析

1.1逃逸分析是什么?

逃逸分析,主要是Go编译器用来决定变量分配在堆或者栈的手段。

区分于C/C++手动管理内存分配,Go将这些工作交给了编译器。

1.2逃逸分析有什么作用

解放程序员。程序员不需要手动指定指针分配内存。

灵活的内存管理。编译器机制可以高效管理内存。

1.3逃逸分析是怎么完成的

编译器根据变量是否被外部引用,决定是否发生逃逸。

如果不被外部引用,则发生逃逸;反之,则发生逃逸。

1.4如何确定是否发生逃逸

运行/编译时,可以指定参数进行查看

1.5Go与C/C++中的堆和栈是同一个概念吗

不是。

C/C++中的堆栈,就是操作系统中传统的堆栈概念。

Go语言中则不同。Go语言中,操作系统中的栈,都提供给了Go运行时,用来处理调度器、垃圾回收、系统调用等。对于用户态的Go代码,消耗的都是操作系统中的堆内存,只是构造出了逻辑上不同的“堆”和“栈”。

二.延迟语句

2.1延迟语句是什么?

延迟语句(defer)是Go语言中注册延迟调用的机制,主要用于成对操作时,如文打开文件/关闭文件、打开连接/关闭连接、加锁/释放锁。

2.2延迟语句的执行顺序是什么?

defer语句会将调用函数压入栈中,先进后出的执行。

在defer函数定义时,对外部变量的引用有2种形式:

1.函数参数:defer定义时会把值传递给defer,并被cache起来。 2.闭包引用:真正执行时根据上下文确定参数值。

2.3如何拆解延迟语句

return xxx

相当于:

1.返回值=xxx

2.调用defer函数

3.return

2.4如何确定延迟语句的参数

判断依据主要与2.2的规则一样。

2.5闭包是什么?

匿名函数也被称为闭包,一个闭包继承了函数声明时的作用域,Go语言中所有的匿名函数都是闭包。

2.6延迟语句如何配合恢复语句

recover()函数只在defer的函数中直接调用才有效。

2.7 todo

2.8为什么无法从父goroutine恢复子goroutine的panic

设计使然。goroutine被设为为一个独立的代码执行单元,拥有自己的执行栈,不与其他goroutine共享任何数据。这意味着,无法让goroutine拥有返回值、也无法让goroutine拥有自身的ID编号等。

三、数据容器

3.1数组与切片

3.1.1数组和切片有何异同

数组是定长的,长度是类型的一部分,所以表达能力有限,在Go语言中不常见。

切片则非常灵活,可以动态扩容,且切片的类型和长度无关。

*底层数组可以被多个切片同时指向,因此对一个切片的元素进行操作,有可能会影响到其他切片。

3.1.2切片如何被截取

基于已有slice创建新slice对象,被称为reslice。新老slice共用底层数组,它们对底层数组的更改都会影响到彼此。

如果因为append操作引起了新slice或者老slice底层数组扩容,则不会相互影响。

这其中的关键就是:两者是否共用底层数组。

 

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 }

 

s1:[2 3 20] s2:[4 5 6 7 100 200] slice:[0 1 2 3 4 5 6 7 100 9]

3.1.3 切片的容量是怎样增长的?

下面的说法是不准确的:

1.当原slice容量小于1024的时候,新slice容量变成原来的2倍

2.当原slice容量超过1024,新slice容量变成原来的1.25倍

实际是:

扩容过程对newcap进行了内存对齐,而这个和内存分配策略有关。进行内存对齐后,新s的容量要大于等于老s容量的2倍或者1.25倍。

3.1.4切片作为函数参数会被改变吗?

slice作为函数参数时,就是一个普通的结构体。直接传slice,实参不受影响;传入slice的指针,则会影响。

Go语言中的函数参数传递,只有值传递,没有引用传递。

3.1.5 内建函数make和new的区别是什么

1.make和new都用来分配内存,但适用类型不同。make适用于slice、map、channel等引用类型,new适用于int、数组、结构体等值类型。

2.make返回一个值,new返回一个指针。

3.make返回初始化之后的类型的引用,new会为类型的新值分配已置零的内存空间,并返回指针。

3.2散列表map

  3.2.1map是什么

map最主要的数据结构有两种:哈希查找表(Hash table)、搜索树(search tree)

哈希查找表用一个哈希函数将key分配到不同的bucket桶,开销主要是哈希函数的计算以及数组的常数访问时间。处理碰撞的方法一般有:链表法和开放地址法。

搜索树一般采用平衡搜索树,包括AVL树、红黑树等。

  3.2.2map的底层原理是什么?

  Go语言使用的是哈希查找表,并且使用链表法解决哈希冲突。

  1.map内存模型

  map的结构体是hmap。关键字段有:

  B:buckets数组的长度的对数,即buckets数组的长度为2^B,bucket里面存储了key和value,bucket是一个指针,指向的是一个结构体:bmap。

  bmap就是人们常说的“桶”,桶里最多装8个<key,value>。

  这些key之所以会落入同一个桶,是因为它们的hash结果是“一类”的,并不是完全相等。hash值的高8位,决定落入桶内的槽位。

每个bucket设计成最多只能放8个key-value对,如果有第9个key-value落入当前的bucket,则需要再构建一个bucket,并通过overflow指针连接起来。这就是所谓的“链表法”。

  2.创建map

创建map,就是调用makemap函数,初始化hmap的各个字段。

slice和map分别作为函数参数数时有什么区别?

在函数内部对map的操作会影响map结构体;而对slice操作则不会。

主要原因:前者是指针(*hmap),后者是结构体(slice)

  3.哈希函数

  Go会检测cpu是否支持aes,如果支持使用aes hash,否则使用memhash。

  4.key定位过程

哈希值共64个bit位(针对64位机),计算元素落入到哪个bucket,只会用到最后B个bit位。

当两个不同的key落入同一个桶中,使用链表法解决哈希冲突,即链表法:从前往后查找第一个空位。

查找时:先找到对应的桶,再去遍历桶中的所有key。如果bucket中没有找到,并且overflow不为空,则会继续在overflow bucket中寻找。

寻找某个key的底层函数是mapacess系列函数。

  5.map的赋值过程是怎样的

  向map插入或者修改key,调用的是mapassign函数。赋值操作的核心仍然是一个双层循环:外层遍历bucket和overflow bucket,内层遍历单个bucket的所有槽位。有比较重要的几点:

  1.写标志flags=1时,说明有其他协程在执行“写”操作,程序会panic,说明map不是协程安全的。

  2.map的扩容是渐进式的,定位元素到某个bucket后,需要确保这个bucket对应的老bucket已经完成了迁移过程(老bucket中的key会被分散到2个新bucket)

  3.定位元素放置的位置时,准备两个指针: inserti和insertk分别指向第一个空的tophash、第一个空闲的cell(槽)

  4.如果触发扩容,则查找定位key的过程会重新执行一次。

  5.最后会更新元素的值,如果是插入的话,map的count字段值加1,hasWriting清零。

  6.map的删除过程是怎样的

  底层执行mapdelete函数,主要逻辑:

  1.检测并发写操作

  2.计算元素的hash值,找到落入的bucket

  3.设置写标志位

  4.检测此map是否在扩容中,如果是,则触发一次搬迁。

  5.两层循环,核心是找到key的具体位置。

  6.找到对应位置后,完成清零操作。

  7.将map的count字段减1,对应位置的tophash改为emptyone。

  8.联动判断是否处理同bucket的其他槽位,emptyOne改成emptyRest的过程。

  7.map的扩容过程是怎样的(todo)
  8.map的遍历过程是怎样的(todo)

  3.2.3map中的key为什么是无序的?

  map在扩容时会触发搬迁。一个bucket中的元素会分散到2个。这个过程不能保证元素的顺序。

  3.2.4map是线程安全的吗?

  不是。如果检测到写标志flags=1则直接panic了。

  3.2.5float类型可以作为map的key吗?

  可以,但是会出现精度丢失问题。float64作为key时,会转成uint64类型,再插入key中。

  3.2.6map如何实现两种get操作

  带comma和不带comma。

  3.2.7如何比较两个map是否相等

  1.都为nil

  2.非空、长度相等,指向同一个map实体对象

  3.相同的key指向value“深度”相等。

  3.2.8可以对map的元素取地址吗?

  不能。

  3.2.9可以边遍历边删除吗?

  同一个协程内边遍历边删除,并不会检测到同时读写,理论上是可以的。

  如果存在多个读写同时进行的情况,推荐使用线程安全的sync.map。

四.通道

4.1CSP是什么

不用通过共享内存来通信,而要通过通信来实现共享内存。

大多数编程语言的并发编程模型是基于线程和内存同步访问控制。Go的并发编程模型则用goroutine和channel来替代。goroutine和线程类似,channel则和mutex类似。

4.2通道有哪些应用

1.停止信号

2.定时任务(结合time包,一般有2种做法,实现超时控制、定时任务)

3.解耦生产方和消费方

4.控制并发数(使用缓存通道)

4.3通道的底结构#todo

  4.3.1数据结构

  4.3.2创建过程

  4.3.3接收过程

  4.3.4发送过程

  4.3.5收发数据的本质

4.4通道的关闭过程发生了什么#todo

4.5从一个关闭的通道里仍然能读出数据吗

从一个有缓冲的channel里读数据,当channel被关闭,已然能读出有效值,只有当返回的ok值为false时,读出的数据才是无效的。

如果是无缓冲的呢?

4.6如何优雅的关闭通道

所谓的优雅关闭channel,就是不关闭channel,让GC代劳。

4.7关于通道的happens-before有哪些?

定义:假设事件a和事件b存在happened-before关系,那么a/b完成后的结果也一定要体现这种关系。

由于现代编译器、CPU会做各种优化,包括编译器重排、内存重排等,在并发代码里,happened-before限制就非常重要了。

4.8通道在什么情况下会引发资源泄露

goroutine操作channel后,处于发送或者接收阻塞状态,而channel处于满或空的状态,一直得不到改变,垃圾回收器并不会回收此类资源。

如果一个channel,没有任何goroutine引用,GC会对其进行回收操作,不会引起内存泄露。

4.9通道的操作情况总结

操作

nil chan

closed chan

not nil & not closed

close

panic

panic

正常关闭

读<-ch

阻塞

对应类型的0值

1.正常读取。

2.缓冲型channel为空阻塞

无缓存型channel无发送者时阻塞

写ch<-

阻塞

panic

1.正常写入

2.缓冲型channel满时阻塞

非缓冲型channel无接收者时阻塞

五.接口

5.1Go接口与C++接口有何异同

Go采用的是“非侵入式”不需要显式声明,只需要定义接口定义的函数,编译器就会自动识别。

5.2Go语言与“鸭子类型”的关系

Go不要求类型显式的声明实现了某个接口,只要实现了相关地方法即可。

5.3iface和eface的区别是什么

iface和eface都是Go中描述接口的底层结构体,区别在于iface描述的接口包含方法,而eface则是不包含任何方法的空接口。

 

type iface struct { tab *itab //接口的类型以及赋值给这个接口的实体类型《动态类型》 data unsafe.Pointer // 指向接口具体的值《动态值》 } type itab struct { inter *interfacetype // 接口类型 _type *_type // 赋值给这个接口的实体类型 ... } type interfacetype struct { typ _type pkgpath name // 接口的包名 mhdr []imethod // 接口定义的函数列表 }

 

type eface struct { _type *_type // 空接口承载的实体类型 data unsafe.Pointer // 具体的值 }

Go语言中各种数据类型都是在_type字段的基础上,增加一些额外的字段来进行管理。

主要包括:类型大小、类型的hash值、内存对齐相关、类型的编号以及GC相关的字段。

5.4值接收者和指针接收者的区别

函数添加一个接收者,它就变成了方法。接收者可以是值接收者,也可以是指针接收者。

实现了接收者是值类型的方法,相当于是自动实现了接收者是指针类型方法。

实现了接收者是指针类型的方法,不会自动生成接收者是值类型的方法。

使用指针作为方法的接收者的理由如下:

1.方法能够修改接收者指向的值

2.避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。

5.5如何用interface实现多态

多态,可以让一种类型具有多种类型的能力。

5.6接口的动态类型和动态值是什么

iface包含两个字段:tab是接口表指针,指向类型信息;data是数据指针。分别被称为动态类型和动态值。

 

var c coder var g *Gopher c = g fmt.Println(c == nil) // false

5.7接口转换的原理是什么

当判定一种类型是否满足某个接口时,Go将类型的方法集和接口所需的方法集进行匹配。

如果类型的方法集,完全包含接口的方法集,则可认为该类型实现了该接口。

5.8类型转换和断言的区别是什么

类型转换、类型断言本质都是把一个类型转换成另外一个类型。不同之处在于,类型断言是对接口变量进行的操作。

对应类型转换,转换前后的两个类型要相互兼容才行。

因为空接口interface{}没有定义任何函数,因此Go中所有类型都实现了空接口。

当一个函数的形参是interface{},那么在函数中,需要对形参进行断言,从而得到它的真实类型。

Go语言中的switch仅执行第一个匹配成功的分支,不需要break语句;另外case不需要是常量,也不必是整数。

fallthrough关键字,表示需要执行下一个分支。

5.9如何让编译器自动检测类型是否实现了接口

var _ io.Writer = (*myWriter)(nil)

六.unsafe

  6.1如何利用unsafe包修改私有成员

对于一个结构体,通过offset函数可以获取结构体成员的偏移量,进而获取成员的地址,读写该地址的内存,就可以达到改变成员值的目的。

结构体被分配一块连续的内存,结构体的地址,也代表了第一个成员的地址。

  6.2如何利用unsafe获取slice和map的长度

  主要通过unsafe.Pointer和uintptr进行转换。

  6.3如何实现字符串和byte切片的零复制转换

 

type StringHeader stuct { Data uintptr Len int } type SliceHeader stuct { Data uintptr Len int Cap int }

只需要共享底层Data和Len就可以实现zero-copy。

七.context

7.1context是什么

context是goroutine的上下文,在goroutine中传递上下文信息,包含:取消信号、超时时间、截止时间、k-v等

7.2context有什么作用

使用context的几点建议:

1.不要讲context塞到结构体里,而是作为第一参数,一般命名为ctx 2.不用向函数传入一个含nil属性的context,可以使用todo代替

3.不用把业务参数塞到context中

4.context是并发安全的

7.3如何使用context

1.传递共享的数据

2.定时取消

3.防止goroutine泄露

7.4context底层原理是什么

主要包含Context和Canceler两个接口。

1.context定义了4个方法,都是冪等的

2.Deadline()返回context的截止时间,决定是否进行后续操作

3.Done()返回一个channel,表示context被取消的信号

4.Err()返回一个错误,表示channel被关闭的原因。例如被取消还是超时

5.Value()获取之前设置的key对应的value

源码中有2个类型实现了canceler接口:*cancelCtx、*timerCtx,注意是指针类型。

设计原因主要是:

  1.“取消”操作应该是建议性,而非强制性

  2.“取消”操作应该可传递

  八、错误

  8.1接口error是什么

  Go使用error类型表示错误,是一个接口类型。

  最简单的是errors.New(),如果需要具体的上下文信息,可以使用fmt.Errorf()

  8.2接口error有什么问题

  Go代码里error满天飞,显得非常冗长拖沓。

  8.3如何理解关于error的三句谚语

  1.视错误为值

  处理error的方式分为三种。

  Sentinel errors:哨兵。处理流程停止。最大的问题在于定义error和使用error的包之间建立了依赖关系,容易引起循环调用。(不推荐)

  Error Types:自定义Error类型,在error基础上,附带其他字段,外层调用者需要使用类型断言来判断错误。也存在循环调用的问题。(不推荐)

  Opaque errors:黑盒error。能知道错误发生了,但是无法看到它内部到底是什么,不知道它的具体类型。

  一旦出错,直接返回错误;否则继续后面的流程。

  如果调用者需要判断返回的错误类型,可以判断错误是否具有某种行为,或者说实现了某个接口。

  这样做的好处是:不需要import引用定义错误的包,并且不需要知道error的具体类型,只需要判断它的行为。即:面向接口编程。

  2.检查并优雅的处理错误

  Go1.13之前使用github.com/pkg/errors,使用Wrap可以将一个错误,加上一个字符串,“包装”成一个新的错误。Cause则是反向操作,将里层的错误还原。

  3.只处理一次错误

  避免函数内和函数外的调用者都处理错误。

  8.4错误处理的改进

  Go1.13支持了error包裹(wrapping),fmt.Errorf增加了%w的格式,并且在error包增加了三个函数:errors.Unwrap、errors.Is、errors.As。

  fmt.Errorf使用%w来生成一个嵌套的error

  Unwrap将嵌套的error解析出来

  Is判断err和target是同一类型,或者error嵌套的error有没有和target同一类型。

  As从错误链中找到第一个和target相等的值,并且设置target指向的变量为err。

九、计时器

  9.1Timer底层数据结构为什么用四叉堆而非二叉堆

  四叉堆和二叉堆本质上没有区别,它使得整体上层数更低,且时间复杂度从O(log2N)降到O(log4N)

  9.2Timer曾做过哪些重大的改进

  9.3定时器的使用场景有哪些

  1.固定时间间隔触发

  2.固定时间间隔重复触发

  3.在某个具体时刻触发

  9.4Timer/Ticker的计时功能有多准确

  影响时间准确性的元素

  1.对系统时间的依赖程度:只能依靠操作系统或者时间提供方(通常认为精度在毫秒级)

  2.对运行时的依赖程度:由于运行时组件的存在,这个时间管理的准确性也将或多或少受到一定程度的影响,例如调度器的调度延迟、垃圾回收器的干扰、操作系统对应用程序进行中断产生的延迟等。(当系统出现可感知的延迟时,可以着重调试运行时本身对延迟的影响,如:调度器任务的数量、Timer/Ticker的密度和垃圾回收器的压力)

  9.5定时器的实现还有哪些方式

  定时器可以使用链表、堆、红黑树等数据结构,也可以使用时间轮实现。流行的高效定时器有三种:Go使用的堆结构、nginx使用的红黑树、linux kernel使用的时间轮。

  十.反射

  10.1反射是什么

  Go语言提供了一个机制,在允许时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的类型,这就是反射机制。

  10.2什么情况下需要使用反射机制

  使用反射的常用场景有以下两种:

  1.不能明确接口调用哪个函数,需要根据传入的参数在运行时决定。

  2.不能明确传入函数的参数类型,需要在运行时处理任意对象。

  不推荐使用反射的原因:

  1.代码经常难以阅读

  2.编译器能提前发现一些类型错误,但对反射代码无能为力。

  3.性能影响较大

  10.3Go语言如何实现反射

  反射是通过接口的类型信息实现的,建立在类型的基础上。

  反射主要与interface{}有关。

  接口变量,可以存储任何实现了接口定义的所有方法的变量。

  GO语言reflect包里定义了一个接口和一个结构体,即reflect.Type和reflect.Value,它们提供很多函数来获取存储在接口里的类型信息。

  reflect.Type提供关于类型相关的信息,和_type关联比较紧密。

  reflect.Value则结合_type和data两者,因此可以获取并改变类型的值。

  reflect包提供了两个基础的关于反射的函数,来获取上述的接口和结构体:TypeOf、ValueOf

  TypeOf函数用来提取一个接口中值的类型信息。

  ValueOf函数返回一个结构体变量,包含类型信息以及实际值。

  反射三大定律

  1.接口类型变量,可以转化成反射类型对象(反射类型对象,指reflect.Type、reflect.Value)

  2.反射类型对象,可以转化成接口类型变量

  3.如果想要操作原变量,反射变量Value必须要持有原变量的地址才行。

  10.4如何比较两个对象是否完全相同

  Go语言中提供了DeepEqual()函数进行比较。参数是两个interface。

  如果是不同的类型,即使是底层类型相同,相应的值也相同,那么两者也不是深度相等。

  10.5如何利用反射实现深度拷贝

  浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象中指针类型的字段还是共享同一块内存。深拷贝:创造一个内容完全相同的对象,新对象与原对象不共享内存,修改新对象不影响原对象。

  实现深度拷贝可以存在多种形式,最简单、最安全也是最容易的方式是使用json.Marshal/Unmarshal。但是涉及到序列化和反序列化,性能较差。通过反射可以实现更高效的深度复制。

  十一.同步模式

    11.1等待组sync.Waitgroup的原理是什么

      sync.Waitgroup可以达到并发goroutine的执行屏障的效果。

      当需要对一个并行执行的代码块引入等待条件时,便可以使用Add操作来产生同步记录;而当不再需要等待条件时,则可在并发代码块中使用Done操作来促成同步屏障条件的达成。

      Waitgroup的内部结构非常简单,内部由3个uint32来对并发的goroutine进行不同目的的计数,分别是运行计数、等待计数和信号计数。并通过state()函数来消除在高层实现上的差异,返回状态(运行计数和等待计数)和信号(信号计数)。

    11.2缓存池sync.pool

    大量重复的创建很多对象,会引起GC的工作量飚升,这时可以使用sync.Pool来缓存对象,减轻对GC的消耗。

    11.3并发安全散列表sync.Map

    Sync.Map是线程安全的,读取、插入、删除也都保持着常数级的时间复杂度。

    Sync.Map的零值是有效的,并且零值是一个空的map,它在第一次使用后,不允许被复制。

    Sync.Map使用非常简单,和普通map相比,仅遍历的方式略有不同。

      sync.Map数据结构:

      mu Mutext 保护read和dirty字段

      read是atomic.Value类型,可以并发地读。

      dirty是一个非线程安全的原始map。

  十二、调度机制

    12.1goroutine和线程有什么区别

  1.内存消耗:goroutine的栈内存消耗为2kb,创建线程需要1MB栈内存

  2.创建和销毁:线程的创建和销毁消耗巨大,是内核级的,通常使用线程池提高复用;goroutine由go runtime负责管理,创建和消费的消耗非常小,是用户级的。

  3.切换:线程切换需要保存各种寄存器,以便恢复;goroutine切换只需保存三个寄存器:PC、Stack Pointer和BP。

    12.2 Go sheduler是什么

    Go程序的执行有两个层面,Go program和Runtime。

  Go schedule是Go运行时最重要的部分。Runtime维护所有的goroutine,并通过schedule进行调度。

  对操作系统而言,只有线程的概念,并不感知goroutine。

  有3个基础的结构体来实现goroutine的调度:G、P、M。

  G:代表goroutine,表示goroutine栈的一些字段等。

  M:代表内核线程,包含正在运行的goroutine等字段

  P:代表一个虚拟的Processor,维护一个处于Runnable状态的goroutine队列。M需要获得P才能运行G。

  还有一个核心结构体:sched,总览全局,负责整个调度器的运行。

  Runtime起始时,会启动一些G:垃圾回收的G,执行调度的G,运行用户代码的G;并且创建一个M用来开始G的运行。

  Go schedule会启动一个后台线程sysmon,来检测长时间(超过10ms)运行的goroutine,将其“停靠”到global runqueues。

  初始化时,Go程序会有一个G,G在M上得到执行,内核线程是在CPU核心上调度,G则在M上进行调度。

  此外,还有两个比较重要的组件:全局可运行队列(GRQ)和本地可运行队列(LRQ)。

  和线程类似,goroutine的状态也有3种:Waiting(等待状态)、Runnable(就绪状态)、Executing(运行状态)。

  12.3goroutine的调度时机有哪些

  4种情形下,goroutine可能会发生调度,但也并不是一定发生。分别是:使用go关键字、GC、系统调用、内存同步访问。

  12.4 M:N模型是什么

  Go runtime会在程序启动后,“按需”创建N个线程,之后创建M个goroutine会依附在N个线程上执行。

  12.5 工作窃取是什么

  Go schedule的职责就是将所有处于runnable的goroutine均匀调度到在P上运行的M。

  当一个P发现自己的LRQ已经没有G时,会从其他P“偷”一些G来运行,这被称为“工作窃取”。

  Go schedule每一轮调度要做的工作,就是找到处于runnable的goroutine,并执行它。顺序如下:

  1.从LRQ中找

  2.从GRQ中找

  3.从netpoll里找

  4.从其他P偷取

  12.6 GPM底层数据结构是怎样的

  G:取的是goroutine的首字母,主要保持goroutine的一些状态信息,以及CPU的一些寄存器的值。G关联了两个比较重要的结构体,stack表示goroutine运行时的栈,gobuf保存PC、SP等寄存器的值。

  M:取的是machine的首字母,代表一个工作线程,或者说系统线程。G需要调度到M上才能运行,M是真正工作的实体。m结构体保存了M自身需要使用的栈信息、正在M上执行的G信息、与之绑定的P信息等。

  P:取processor的首字母,为M的执行提供“上下文”,保存M执行G时的一些资源,例如本地可运行G队列,memeory cache等。一个M只有绑定P才能执行goroutine,当M被阻塞时,整个P会被传递给其他M。

  12.7schedule的初始化过程是怎样的

  Go scheduler在源码中的结构体为schedt,保存调度器的状态信息、全局的可运行G队列等。

  12.9g0栈和用户栈如何被切换

  g0栈用于执行调度器的代码,它选择一个可运行的goroutine,之后跳转到执行用户代码的地方。如何跳转,这中间涉及栈和寄存器的切换。函数调用和返回主要靠的也是CPU寄存器的切换,goroutine的切换和此类似。

  12.13M如何找工作

  1.从本地队列找、2.定期从全局队列找、最后从别的P偷取。

  12.14系统监控sysmon后台监控线程做了什么

  1.抢占处于系统调用的P,让其他M接管它,以运行其他的goroutine

  2.将运行时间过长的goroutine调度出去,给其他goroutine运行的机会

  十三、内存分配机制

    13.1管理内存的动机是什么,通常涉及哪些组件

    内存管理的动机:性能要求、解放程序员

    内存管理运行时的组件:

    1.页分配器:从操作系统申请内存

    2.对象分配器:为用户程序分配内存

    3.垃圾回收期:回收用户程序所分配的内存

    4.拾荒器:向操作系统归还申请的内存

    从运行时对内存的管理角度来看,内存有4种状态:空状态(None)预留态(Reserved)准备态(Prepared)以及就绪态(Ready)

    13.2Go语言中的堆和栈概念与传统意义上的堆和栈有什么区别

    运行时中的mheap结构存储了整个go堆的管理状态,涉及页分配器和对象分配器。从两个分配器视角可以将内存考虑为两种不同的粒度单位。

    页分配器:堆是按照连续的页进行管理。

    对象分配器:以跨度(span)进行管理。一个跨度以mspan结构进行存储,每个跨度可以存储多个分配的对象,并由多个连续的页组成。

    13.3对象分配器是如何实现的

    分配的基本策略:顺序分配和自由表分配两大策略。

    顺序分配:直接从一段连续空间的一端开始,按需逐次将内存分配给用户程序。(Go堆内存使用)

    自由表分配:使用链表结构来维护未分配的内存,进行串联管理。(运行时对象所在的非托管内存使用)

    对象分配的缓存分为两个:

    1.本地跨度缓存:不需要分配新的跨度 2.中枢跨度缓存:需要分配新的跨度

    _type是Go类型的实现,通过size属性可以获得该类型对应的大小。对象分配的流程分为三种基本情况:

    1.微对象分配:针对小于16B的对象分配请求

    2.小对象分配:针对大小介于16B和32KB之间的分配请求

    3.大对象分配:针对大于32KB的对象分配请求

十四、垃圾回收机制

14.1垃圾回收的认识

垃圾回收,是一种自动内存管理的机制。

当程序向操作系统申请的内存不再需要时,垃圾回收主动将其回收,并供其他代码申请内存复用,或者将其归还给操作系统,这个过程称为垃圾回收。

垃圾回收器的执行过程被划分为两个半独立的组件:

1.赋值器:代指用户态的代码,对垃圾回收器而言,用户态的代码只修改对象之间的引用关系。

2.回收器:负责执行垃圾回收的代码。

垃圾回收器在标记过程中,最先检查的对象包括:全局变量、执行栈、寄存器。

GC算法的存在形式,可以归结为:追踪和引用计数这两种形式的混合运用。

追踪式GC:从根对象出发,根据引用关系逐步扫描,确定保留的对象,从而回收所有可回收的对象。

引用计数式GC:每个对象自身包含一个被引用的计数器,计数器归零时自动回收。

三色标记法是什么?

关键是理解三色抽象以及波面推进两个概念。

三色抽象规定了三种不同类型的对象,并以不同颜色相称。

1.白色对象:未被回收器访问,初始颜色,回收结束后,均不可达。

2.灰色对象:已被回收器访问,可能指向白色对象。

3.黑色对象:已被回收器访问,所有字段已被扫描,黑色对象中任何一个指针都不可能指向白色对象。

STW是什么?

stop the world,也可以是start the world。从stop the world到start the world的这段时间间隔。

垃圾回收过程,为了保证实现的正确性,防止无止境的内存增长。

相关文章:

“Go程序员面试笔试宝典”复习便签

一.逃逸分析 1.1逃逸分析是什么&#xff1f; 逃逸分析&#xff0c;主要是Go编译器用来决定变量分配在堆或者栈的手段。 区分于C/C手动管理内存分配&#xff0c;Go将这些工作交给了编译器。 1.2逃逸分析有什么作用 解放程序员。程序员不需要手动指定指针分配内存。 灵活的…...

数组的度(指数组里任一元素出现频数的最大值)

题目&#xff1a; 给定一个非空且只包含非负数的整数数组 nums&#xff0c;数组的 度 的定义是指数组里任一元素出现频数的最大值。 你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组&#xff0c;返回其长度。 示例 1&#xff1a; 输入&#xff1a;nums …...

scala array类型参数

在Scala中&#xff0c;数组&#xff08;Array&#xff09;是一种用于存储相同类型元素的数据结构。数组可以用于保存基本数据类型和自定义数据类型的元素。当定义数组类型参数时&#xff0c;您通常是在函数、类或方法签名中使用它们。以下是一些有关Scala数组类型参数的示例&am…...

构建 NodeJS 影院预订微服务并使用 docker 部署(03/4)

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 你好社区&#xff0c;这是&#x1f3f0;“构建 NodeJS 影院微服务”系列的第三篇文章。本系列文章演示了…...

html写一个向flask_socketio发送消息和接收消息并显示在页面上

以下是一个简单的HTML页面&#xff0c;它包含一个输入框、一个发送按钮和一个显示区域。用户可以在输入框中输入消息&#xff0c;点击发送按钮&#xff0c;然后这个消息会被发送到 Flask-SocketIO 服务器。当服务器回应消息时&#xff0c;它会在页面的显示区域显示出来。 <…...

C#使用.Net Core进行跨平台开发

使用 .NET Core 进行跨平台开发是一种灵活的方法&#xff0c;可以在多个操作系统上运行 C# 应用程序。以下是在 C# 中使用 .NET Core 进行跨平台开发的一般步骤&#xff1a; 安装 .NET Core SDK&#xff1a; 在开始之前&#xff0c;需要安装适用于操作系统的 .NET Core SDK。可…...

Java“牵手”天猫店铺所有商品API接口数据,通过店铺ID获取整店商品详情数据,天猫API申请指南

天猫商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。天猫商品详情可以帮助消费者更好的了解宝贝信息&#xff0c;从而做出购买决策。同时&#xff0c;消费者也可以通过商品详情了解其他买家对宝贝的评价&#xf…...

php输入post过滤函数,入库出库,显示

第一部分 php输入post过滤函数 function GLOBAL_POST($str) {$str_origin$str; if (empty($str)) return false;$str str_replace( /, "", $str);//替换关键词 $str str_replace("\\", "", $str); $str str_replace("&gt", &…...

matlab-对数据集加噪声并实现tsne可视化

matlab-对数据集加噪声并实现tsne可视化 最近才知道&#xff0c;原来可以不用模型&#xff0c;也能实现对数据集数据的可视化。 **一、**以COIL-100数据集为例子。 问题&#xff1a; 前提&#xff1a;首先对COIL-100数据集根据角度0-175和180-255&#xff0c;分别划分成C1,C…...

【BASH】回顾与知识点梳理(三十八)

【BASH】回顾与知识点梳理 三十八 三十八. 源码概念及简单编译38.1 开放源码的软件安装与升级简介什么是开放源码、编译程序与可执行文件什么是函式库什么是 make 与 configure什么是 Tarball 的软件如何安装与升级软件 38.2 使用传统程序语言进行编译的简单范例单一程序&#…...

Sql注入攻击的三种方式

SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。SQL 注…...

dockerfile部署前端vue打包的dist文件实战

背景&#xff1a;一般前端开发后会将打包后的dist文件交由我们部署&#xff0c;部署的方式有很多&#xff0c;这里提供一种思路 在服务器的路径下新建一个目录&#xff0c;在目录中新建Dockerfile&#xff0c;编辑这个文件 FROM nginxCOPY ./dist /home/front COPY nginx.con…...

[技术杂谈]MobaXterm中文乱码编码问题一种解决方法

今日使用mobaxterm连接树莓派发现安装出现乱码&#xff0c;看不清文字是什么。最最简单方式是ssh设置终端字体&#xff0c;具体步骤为&#xff1a; 1. 右键会话&#xff0c;点击编辑会话 2.在以下画面点击终端字体设置 3.选择编码&#xff1a;GBK或者ISO-8859-1...

mac os M1 安装并启动 postgreSQL 的问题

Homebrew 安装 postgreSQL brew install postgresql启动 brew services start postgresql但报错&#xff1a; uninitialized constant Homebrew::Service::System解决方案 brew doctor按照 brew doctor 中的建议进行操作&#xff0c;如果不行&#xff0c;如下&#xff1a; h…...

如何使用Wireshark进行网络流量分析?

如何使用Wireshark进行网络流量分析。Wireshark是一款强大的网络协议分析工具&#xff0c;可以帮助我们深入了解网络通信和数据流动。 1. 什么是Wireshark&#xff1f; Wireshark是一个开源的网络协议分析工具&#xff0c;它可以捕获并分析网络数据包&#xff0c;帮助用户深入…...

抖音web主页视频爬虫

需要抖音主页视频爬虫源码的发私信&#xff0c;小偿即可获得长期有效的采集程序。 比构造 s_v_web_id 验证滑块的方法更快&#xff0c;更稳定。...

常用的jar包【maven坐标格式】

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f3e8;mysql加载启动项jar包&#x1f993;mybatis加载启动项jar包 &#x1f412;个人主页 &#x1f3c5;JavaEE系列专栏 &#x1f4d6;前言&#xff1a; 本篇博客主要以介绍常…...

【分布式】VMware FT概要

讨论了1primary 1backup的情况&#xff0c;比较好理解&#xff0c; 6.824中以该论文为例&#xff0c;介绍了分布式系统中复制的概念&#xff0c;复制的方式。以下简要讲述一些关键点&#xff0c;来源于MIT6.824课程&#xff0c;翻译版本 https://mit-public-courses-cn-transl…...

江西南昌电气机械三维测量仪机械零件3d扫描-CASAIM中科广电

精密机械零部件是指机械设备中起到特定功能的零件&#xff0c;其制造精度要求非常高。这些零部件通常由金属、塑料或陶瓷等材料制成&#xff0c;常见的精密机械零部件包括齿轮、轴承、螺丝、活塞、阀门等。精密机械零部件的制造需要高精度的加工设备和工艺&#xff0c;以确保其…...

MySQL三大日志(binlog、redo log和undo log)详解

1.redo log redo log是InnoDB存储引擎层的日志&#xff0c;又称重做日志文件。 用于记录事务操作的变化&#xff0c;记录的是数据修改之后的值&#xff0c;不管事务是否提交都会记录下来 redo log包括两部分&#xff1a;一个是内存中的日志缓冲(redo log buffer)&#xff0c;另…...

七大排序算法详解

1.概念 1.排序的稳定性 常见的稳定的排序有三种&#xff1a;直接插入排序&#xff0c;冒泡排序&#xff0c;归并排序 对于一组数据元素排列&#xff0c;使用某种排序算法对它进行排序&#xff0c;若相同数据之间的前后位置排序后和未排序之前是相同的&#xff0c;我们就成这种…...

[docker][WARNING]: Empty continuation line found in:

报警内容&#xff1a; 下面展示一些 内联代码片。 //执行 sudo docker build ubuntu:v1.00 . [WARNING]: Empty continuation line found in:出现上述错误原因为18行多了一个 " \" 符号&#xff0c;去除即可...

探工业互联网的下一站!腾讯云助力智造升级

引言 数字化浪潮正深刻影响着传统工业形态。作为第四次工业革命的重要基石&#xff0c;工业互联网凭借其独特的价值快速崛起&#xff0c;引领和推动着产业变革方向。面对数字化时代给产业带来的机遇与挑战&#xff0c;如何推动工业互联网的规模化落地&#xff0c;加速数字经济…...

SpringBoot上传文件的实现与优化

一、什么是文件上传&#xff1f; 文件上传是指客户端将本地的文件通过HTTP协议发送到服务器端的过程。文件上传是Web开发中常见的功能之一&#xff0c;例如用户可以上传头像、照片、视频、文档等各种类型的文件。文件上传涉及到客户端和服务器端的交互&#xff0c;需要考虑文件…...

学习python可以做什么?有前景么

Python被热门领域广泛应用 学习者就业优势明显&#xff01; 说到Python的优势&#xff0c;就不得不提这句玩笑话&#xff1a;Python除了不会生孩子&#xff0c;其他的都会。 Web开发、网络爬虫、数据分析、人工智能、自动化、云计算、网络编程、游戏开发等领域&#xff0c;统…...

还不知道怎么提示LLM?ChatGPT提示入门

文章目录 简介&#xff1a;什么是人工智能&#xff1f;什么是提示过程&#xff1f;为什么会出现这样的差异&#xff1f; 为什么需要提示过程&#xff1f;1) 文章摘要2) 数学问题求解 如何进行提示过程&#xff1f;角色提示&#xff1a;多范例提示&#xff1a;无范例提示单范例提…...

反射机制-体会反射的动态性案例(尚硅谷Java学习笔记)

// 举例01 public class Reflect{ // 静态性 public Person getInstance(){return new Person(); }// 动态性 public T<T> getInstance(String className) throws Exception{Calss clzz Class.forName(className);Constructor con class.getDeclaredConstructor();con…...

uniapp离线打包apk - Android Studio

uniapp 离线打包 基于uni-app的andiord 离线打包 开发工具及所需要的jar包​1.将下载的App离线SDK解压打开&#xff0c;找到HBuilder-Integrate-AS &#xff0c;在Android Studio打开2.打开HBuilder X&#xff0c;发行->原生app本地打包->生成本地打包app资源3.在“HBuil…...

cuda面试准备(一),架构调试

1 cuda架构 硬件方面 SP (streaming Process) ,SM (streaming multiprocessor) 是硬件(GPUhardware) 概念。而thread,block,grid,warp是软件上的(CUDA) 概念 SP:最基本的处理单元,streaming processor,也称为CUDA core,最后具体的指令和任务都是在SP上处理的。GPU进行并行…...

docker containers logs清理

容器的磁盘占用 每次创建一个容器时&#xff0c;都会有一些文件和目录被创建&#xff0c;例如&#xff1a; /var/lib/docker/containers/ID目录&#xff0c;如果容器使用了默认的日志模式&#xff0c;他的所有日志都会以JSON形式保存到此目录下。 /var/lib/docker/overlay2 目…...

Ubuntu安装RabbitMQ

一、安装 更新系统软件包列表&#xff1a; sudo apt update安装RabbitMQ的依赖组件和GPG密钥&#xff1a; sudo apt install -y curl gnupg curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo gpg --dearmo…...

Vue3获取当前环境信息

获取.env.development和.env.production内的信息及环境信息 在业务逻辑文件中可以通过 import.meta.env的方式获取&#xff0c;例如&#xff1a; const { MODE, VITE_APP_BASE_API} import.meta.env在vite.config.js中获取&#xff1a; import { defineConfig, loadEnv } f…...

Linux 系统 diff 文件比较命令详解

diff 命令用于比较两个文件或目录之间的差异。它会逐行比较文件的内容&#xff0c;并且在不同的行上显示不同之处。下面是 diff 命令的使用方法和选项&#xff1a; 基本语法&#xff1a; diff [选项] 文件1 文件2常见选项&#xff1a; -c 或 --context&#xff1a;显示上下文…...

【负载均衡】Nacos简单入门

Nacos简单入门 快速安装 在Nacos的GitHub页面&#xff0c;提供有下载链接&#xff0c;可以下载编译好的Nacos服务端或者源代码&#xff1a; 下载完压缩包之后&#xff0c;放在任意目录下面进行解压&#xff1a; GitHub主页&#xff1a;https://github.com/alibaba/nacos G…...

实验一 ubuntu 网络环境配置

ubuntu 网络环境配置 【实验目的】 掌握 ubuntu 下网络配置的基本方法&#xff0c;能够通过有线网络连通 ubuntu 和开发板 【实验环境】 ubuntu 14.04 发行版FS4412 实验平台 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行&#xff0c;以“#”开头的…...

ubuntu can应用开发环境搭建指南

sudo apt-get update sudo apt-get install can-utils libsocketcan-dev can数据发送这个采用来自网上的一段代码进行测试: can_send.c代码内容如下: /* 1. 报文发送程序 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <…...

全流程R语言Meta分析核心技术高阶应用

查看原文>>>全流程R语言Meta分析核心技术高阶应用 目录 专题一、Meta分析的选题与检索 专题二、Meta分析与R语言数据清洗及统计方法 专题三、R语言Meta分析与作图 专题四、R语言Meta回归分析 专题五、R语言Meta诊断分析 专题六、R语言Meta分析的不确定性 专题…...

windows下安装使用git-lfs克隆大文件

下载安装git-lfs工具 首先去git-lfs这里&#xff0c;下载相应平台的工具&#xff0c;我下载的windows版本&#xff0c;非安装版本&#xff0c;直接配置到系统环境变量里 执行以下命令验证是否成功 git lfs install 克隆数据集 这样自动会下载里边的大文件 git clone https:/…...

Node.js下载安装及环境配置教程

一、进入官网地址下载安装包 https://nodejs.org/zh-cn/download/ 选择对应你系统的Node.js版本&#xff0c;这里我选择的是Windows系统、64位 Tips&#xff1a;如果想下载指定版本&#xff0c;点击【以往的版本】&#xff0c;即可选择自己想要的版本下载 二、安装程序 &a…...

半导体低压热氧工艺中的真空度精密控制解决方案

摘要&#xff1a;在目前的各种半导体材料热氧化工艺中&#xff0c;往往需要对正负压力进行准确控制并对温度变化做出快速的响应&#xff0c;为此本文提出了热氧化工艺的正负压力控制解决方案。解决方案的核心是基于动态平衡法分别对进气和排气流量进行快速调节&#xff0c;具体…...

TCP的可靠性之道:确认重传和流量控制

TCP 全称为 Transmission Control Protocol&#xff08;传输控制协议&#xff09;&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;其中可靠性是相对于其他传输协议的优势点。TCP 为了确保数据传输的可靠性主要做了以下几点&#xff1a; 发送确…...

基于spring boot校园疫情信息管理系统/疫情管理系统

摘要 随着计算机技术&#xff0c;网络技术的迅猛发展&#xff0c;Internet 的不断普及&#xff0c;网络在各个领域里发挥了越来越重要的作用。特别是随着近年人民生活水平不断提高&#xff0c;校园疫情信息管理系统给学校带来了更大的帮助。 由于当前疫情防控形势复杂&#xff…...

使用Python批量将飞书文档转为MD

说明&#xff1a;飞书是在线文档平台&#xff0c;本文介绍如何使用Python程序批量将飞书文档转为MD文档&#xff0c;并下载到本地&#xff1b; 复制地址 首先&#xff0c;把文档的URL都复制下来&#xff0c;这个需要一个一个点&#xff0c;并复制拷贝&#xff0c;但却是工作量…...

Nacos配置管理、Feign远程调用、Gateway服务网关

1.Nacos配置管理 1.1.将配置交给Nacos管理的步骤 1.在Nacos中添加配置 Data Id服务名称-环境名称.yaml eg&#xff1a;userservice-dev.yaml 2.引入nacos-config依赖 在user-service服务中&#xff0c;引入nacos-config的客户端依赖 <!--nacos配置管理依赖--> <dep…...

解决Spring Boot前后端分离开发模式中的跨域问题

在实际开发中&#xff0c;经常会遇到前端Vue应用与后端Spring Boot API接口存在跨域访问的问题。本篇博客将分享解决Spring Boot前端Vue跨域问题的实战经验&#xff0c;帮助开发者快速解决该问题。 一、跨域问题的原因 跨域问题是由于浏览器的同源策略引起的。同源策略限制了…...

常见前端面试之VUE面试题汇总五

13. assets 和 static 的区别 相同点&#xff1a; assets 和 static 两个都是存放静态资源文件。项目中所 需要的资源文件图片&#xff0c;字体图标&#xff0c;样式文件等都可以放在这两个文件 下&#xff0c;这是相同点 不相同点&#xff1a;assets 中存放的静态资源文件在…...

带着问题看SpringBoot

带着问题看SpringBoot 1、Spring容器具体是什么&#xff1f; 跟进run方法&#xff0c;context this.createApplicationContext()&#xff0c;得出容器是AnnotationConfigServletWebServerApplicationContext类。 SpringApplication.run(ServeroneApplication.class, args);…...

【Go 基础篇】Go语言匿名函数详解:灵活的函数表达式与闭包

介绍 在Go语言中&#xff0c;函数是一等公民&#xff0c;这意味着函数可以像其他类型的值一样被操作、传递和赋值。匿名函数是一种特殊的函数&#xff0c;它没有固定的函数名&#xff0c;可以在代码中被直接定义和使用。匿名函数在Go语言中具有重要的地位&#xff0c;它们常用…...

MobileNet、MobileNetV2和MobileNetV3创新点总结

当谈论MobileNet、MobileNetV2和MobileNetV3时&#xff0c;我们指的是一系列基于深度学习的轻量级神经网络架构&#xff0c;这些架构旨在在保持高度准确性的同时减少模型的计算和参数量。以下是它们各自的创新点的详细总结&#xff1a; MobileNet&#xff1a; 深度可分离卷积&…...

算法:数据转换处理2(云台显控)

#define DISPLAYFUNC #include"define.h" extern OS_EVENT *KEYMsg; uchar mBlank[21] = " " ; u c h a r s t r v g a [ ] = " 0.00 V "; uchar str_vga[] = "0.00V...